Use vitest for some EW unit tests (#33816)

* Use vitest for some EW unit tests

* Ensure library builds are done before unit tests

* Stabilise jest tests

* Move more tests over

* Make sonar happier

* Update types/node for happy-dom compat again

* Decrease max-workers to stabilise jest tests

* Split jest over 3 runners to alleviate memory woes

* Switch jest to runInBand

* Attempt to deflake jest tests

* tweak coverage

* tweak coverage
This commit is contained in:
Michael Telatynski
2026-06-15 13:24:33 +00:00
committed by GitHub
parent 7949980a7e
commit 2f4e6a4ec4
37 changed files with 475 additions and 326 deletions
+3 -7
View File
@@ -35,7 +35,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
# Run multiple instances in parallel to speed up the tests # Run multiple instances in parallel to speed up the tests
runner: [1, 2] runner: [1, 2, 3]
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
@@ -61,24 +61,20 @@ jobs:
path: /tmp/jest_cache path: /tmp/jest_cache
key: ${{ hashFiles('**/pnpm-lock.yaml') }} key: ${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Get number of CPU cores
id: cpu-cores
uses: SimenB/github-actions-cpu-cores@97330871fe1b7d3529392ea000e3d2c4b357e403 # v3
- name: Run tests - name: Run tests
working-directory: apps/web working-directory: apps/web
run: | run: |
pnpm test \ pnpm test \
--coverage=$ENABLE_COVERAGE \ --coverage=$ENABLE_COVERAGE \
--ci \ --ci \
--max-workers $MAX_WORKERS \ --maxWorkers="50%" \
--shard "$SHARD" \ --shard "$SHARD" \
--cacheDirectory /tmp/jest_cache --cacheDirectory /tmp/jest_cache
env: env:
# tell jest to use coloured output # tell jest to use coloured output
FORCE_COLOR: true FORCE_COLOR: true
MAX_WORKERS: ${{ steps.cpu-cores.outputs.count }}
SHARD: ${{ format('{0}/{1}', matrix.runner, strategy.job-total) }} SHARD: ${{ format('{0}/{1}', matrix.runner, strategy.job-total) }}
NODE_OPTIONS: "--max_old_space_size=8192"
- name: Move coverage files into place - name: Move coverage files into place
if: env.ENABLE_COVERAGE == 'true' if: env.ENABLE_COVERAGE == 'true'
+1 -1
View File
@@ -232,7 +232,7 @@ module.exports = {
}, },
}, },
{ {
files: ["test/**/*.{ts,tsx}", "playwright/**/*.ts"], files: ["src/**/*.test.{ts,tsx}", "test/**/*.{ts,tsx}", "playwright/**/*.ts"],
extends: ["plugin:matrix-org/jest"], extends: ["plugin:matrix-org/jest"],
rules: { rules: {
// We don't need super strict typing in test utilities // We don't need super strict typing in test utilities
+4 -1
View File
@@ -52,16 +52,19 @@ const config: Config = {
], ],
collectCoverageFrom: [ collectCoverageFrom: [
"<rootDir>/src/**/*.{js,ts,tsx}", "<rootDir>/src/**/*.{js,ts,tsx}",
"<rootDir>/packages/**/*.{js,ts,tsx}",
// getSessionLock is piped into a different JS context via stringification, and the coverage functionality is // getSessionLock is piped into a different JS context via stringification, and the coverage functionality is
// not available in that contest. So, turn off coverage instrumentation for it. // not available in that contest. So, turn off coverage instrumentation for it.
"!<rootDir>/src/utils/SessionLock.ts", "!<rootDir>/src/utils/SessionLock.ts",
// Coverage chokes on type definition files // Coverage chokes on type definition files
"!<rootDir>/src/**/*.d.ts", "!<rootDir>/src/**/*.d.ts",
// Ignore vitest tests
"!<rootDir>/src/**/*.test.{ts,tsx}",
"!<rootDir>/src/test/**",
], ],
coverageReporters: ["text-summary", ["lcov", { projectRoot: "../../" }]], coverageReporters: ["text-summary", ["lcov", { projectRoot: "../../" }]],
prettierPath: null, prettierPath: null,
moduleDirectories: ["node_modules", "test/test-utils"], moduleDirectories: ["node_modules", "test/test-utils"],
workerIdleMemoryLimit: "512MB",
}; };
// if we're running under GHA, enable relevant reporters // if we're running under GHA, enable relevant reporters
+4
View File
@@ -30,6 +30,7 @@
"lint:types": "nx lint:types", "lint:types": "nx lint:types",
"lint:style": "stylelint \"res/css/**/*.pcss\"", "lint:style": "stylelint \"res/css/**/*.pcss\"",
"test": "nx test:unit", "test": "nx test:unit",
"test:vitest": "nx test:vitest",
"test:playwright": "nx test:playwright --", "test:playwright": "nx test:playwright --",
"test:playwright:open": "nx test:playwright -- --ui", "test:playwright:open": "nx test:playwright -- --ui",
"test:playwright:screenshots": "nx test:playwright:screenshots --", "test:playwright:screenshots": "nx test:playwright:screenshots --",
@@ -129,6 +130,7 @@
"@element-hq/element-call-embedded": "0.20.1", "@element-hq/element-call-embedded": "0.20.1",
"@element-hq/element-web-playwright-common": "workspace:*", "@element-hq/element-web-playwright-common": "workspace:*",
"@fetch-mock/jest": "^0.2.20", "@fetch-mock/jest": "^0.2.20",
"@fetch-mock/vitest": "^0.2.18",
"@jest/globals": "^30.2.0", "@jest/globals": "^30.2.0",
"@peculiar/webcrypto": "^1.4.3", "@peculiar/webcrypto": "^1.4.3",
"@playwright/test": "catalog:", "@playwright/test": "catalog:",
@@ -190,6 +192,7 @@
"express": "^5.0.0", "express": "^5.0.0",
"fake-indexeddb": "^6.0.0", "fake-indexeddb": "^6.0.0",
"file-loader": "^6.0.0", "file-loader": "^6.0.0",
"happy-dom": "^20.10.2",
"html-webpack-plugin": "^5.5.3", "html-webpack-plugin": "^5.5.3",
"identity-obj-proxy": "^3.0.0", "identity-obj-proxy": "^3.0.0",
"jest": "^30.0.0", "jest": "^30.0.0",
@@ -226,6 +229,7 @@
"testcontainers": "^12.0.0", "testcontainers": "^12.0.0",
"typescript": "catalog:", "typescript": "catalog:",
"util": "^0.12.5", "util": "^0.12.5",
"vitest": "catalog:",
"web-streams-polyfill": "^4.0.0", "web-streams-polyfill": "^4.0.0",
"webpack": "^5.89.0", "webpack": "^5.89.0",
"webpack-bundle-analyzer": "^5.0.0", "webpack-bundle-analyzer": "^5.0.0",
+10 -1
View File
@@ -46,11 +46,20 @@
}, },
"dependsOn": ["^build", "^build:playwright"] "dependsOn": ["^build", "^build:playwright"]
}, },
"test:unit:prepare": {
"executor": "nx:noop",
"dependsOn": ["^build"]
},
"test:unit": { "test:unit": {
// We avoid the jest executor because it doesn't seem to give any benefit, and it mangles the summary of failing tests. // We avoid the jest executor because it doesn't seem to give any benefit, and it mangles the summary of failing tests.
"command": "jest", "command": "jest",
"options": { "cwd": "apps/web" }, "options": { "cwd": "apps/web" },
"dependsOn": ["^build"] "dependsOn": ["test:unit:prepare"]
},
"test:vitest": {
"command": "vitest run",
"options": { "cwd": "apps/web" },
"dependsOn": ["test:unit:prepare"]
}, },
"test:playwright": { "test:playwright": {
"command": "playwright test", "command": "playwright test",
@@ -5,9 +5,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. Please see LICENSE files in the repository root for full details.
*/ */
import EventEmitter from "events"; // @vitest-environment happy-dom
import UserActivity from "../../src/UserActivity"; import { vi, describe, it, expect, beforeEach, afterEach } from "vitest";
import EventEmitter from "node:events";
import UserActivity from "./UserActivity";
class FakeDomEventEmitter extends EventEmitter { class FakeDomEventEmitter extends EventEmitter {
addEventListener(what: string, l: (...args: any[]) => void) { addEventListener(what: string, l: (...args: any[]) => void) {
@@ -29,7 +32,7 @@ describe("UserActivity", function () {
fakeDocument = new FakeDomEventEmitter(); fakeDocument = new FakeDomEventEmitter();
userActivity = new UserActivity(fakeWindow as unknown as Window, fakeDocument as unknown as Document); userActivity = new UserActivity(fakeWindow as unknown as Window, fakeDocument as unknown as Document);
userActivity.start(); userActivity.start();
jest.useFakeTimers(); vi.useFakeTimers();
}); });
afterEach(function () { afterEach(function () {
@@ -49,7 +52,7 @@ describe("UserActivity", function () {
}); });
it("should not consider user active after activity if no window focus", function () { it("should not consider user active after activity if no window focus", function () {
fakeDocument.hasFocus = jest.fn().mockReturnValue(false); fakeDocument.hasFocus = vi.fn().mockReturnValue(false);
userActivity.onUserActivity({ type: "event" } as Event); userActivity.onUserActivity({ type: "event" } as Event);
expect(userActivity.userActiveNow()).toBe(false); expect(userActivity.userActiveNow()).toBe(false);
@@ -57,62 +60,62 @@ describe("UserActivity", function () {
}); });
it("should consider user active shortly after activity", function () { it("should consider user active shortly after activity", function () {
fakeDocument.hasFocus = jest.fn().mockReturnValue(true); fakeDocument.hasFocus = vi.fn().mockReturnValue(true);
userActivity.onUserActivity({ type: "event" } as Event); userActivity.onUserActivity({ type: "event" } as Event);
expect(userActivity.userActiveNow()).toBe(true); expect(userActivity.userActiveNow()).toBe(true);
expect(userActivity.userActiveRecently()).toBe(true); expect(userActivity.userActiveRecently()).toBe(true);
jest.advanceTimersByTime(200); vi.advanceTimersByTime(200);
expect(userActivity.userActiveNow()).toBe(true); expect(userActivity.userActiveNow()).toBe(true);
expect(userActivity.userActiveRecently()).toBe(true); expect(userActivity.userActiveRecently()).toBe(true);
}); });
it("should consider user not active after 10s of no activity", function () { it("should consider user not active after 10s of no activity", function () {
fakeDocument.hasFocus = jest.fn().mockReturnValue(true); fakeDocument.hasFocus = vi.fn().mockReturnValue(true);
userActivity.onUserActivity({ type: "event" } as Event); userActivity.onUserActivity({ type: "event" } as Event);
jest.advanceTimersByTime(10000); vi.advanceTimersByTime(10000);
expect(userActivity.userActiveNow()).toBe(false); expect(userActivity.userActiveNow()).toBe(false);
}); });
it("should consider user passive after 10s of no activity", function () { it("should consider user passive after 10s of no activity", function () {
fakeDocument.hasFocus = jest.fn().mockReturnValue(true); fakeDocument.hasFocus = vi.fn().mockReturnValue(true);
userActivity.onUserActivity({ type: "event" } as Event); userActivity.onUserActivity({ type: "event" } as Event);
jest.advanceTimersByTime(10000); vi.advanceTimersByTime(10000);
expect(userActivity.userActiveRecently()).toBe(true); expect(userActivity.userActiveRecently()).toBe(true);
}); });
it("should not consider user passive after 10s if window un-focused", function () { it("should not consider user passive after 10s if window un-focused", function () {
fakeDocument.hasFocus = jest.fn().mockReturnValue(true); fakeDocument.hasFocus = vi.fn().mockReturnValue(true);
userActivity.onUserActivity({ type: "event" } as Event); userActivity.onUserActivity({ type: "event" } as Event);
jest.advanceTimersByTime(10000); vi.advanceTimersByTime(10000);
fakeDocument.hasFocus = jest.fn().mockReturnValue(false); fakeDocument.hasFocus = vi.fn().mockReturnValue(false);
fakeWindow.emit("blur", {}); fakeWindow.emit("blur", {});
expect(userActivity.userActiveRecently()).toBe(false); expect(userActivity.userActiveRecently()).toBe(false);
}); });
it("should not consider user passive after 3 mins", function () { it("should not consider user passive after 3 mins", function () {
fakeDocument.hasFocus = jest.fn().mockReturnValue(true); fakeDocument.hasFocus = vi.fn().mockReturnValue(true);
userActivity.onUserActivity({ type: "event" } as Event); userActivity.onUserActivity({ type: "event" } as Event);
jest.advanceTimersByTime(3 * 60 * 1000); vi.advanceTimersByTime(3 * 60 * 1000);
expect(userActivity.userActiveRecently()).toBe(false); expect(userActivity.userActiveRecently()).toBe(false);
}); });
it("should extend timer on activity", function () { it("should extend timer on activity", function () {
fakeDocument.hasFocus = jest.fn().mockReturnValue(true); fakeDocument.hasFocus = vi.fn().mockReturnValue(true);
userActivity.onUserActivity({ type: "event" } as Event); userActivity.onUserActivity({ type: "event" } as Event);
jest.advanceTimersByTime(1 * 60 * 1000); vi.advanceTimersByTime(1 * 60 * 1000);
userActivity.onUserActivity({ type: "event" } as Event); userActivity.onUserActivity({ type: "event" } as Event);
jest.advanceTimersByTime(1 * 60 * 1000); vi.advanceTimersByTime(1 * 60 * 1000);
userActivity.onUserActivity({ type: "event" } as Event); userActivity.onUserActivity({ type: "event" } as Event);
jest.advanceTimersByTime(1 * 60 * 1000); vi.advanceTimersByTime(1 * 60 * 1000);
expect(userActivity.userActiveRecently()).toBe(true); expect(userActivity.userActiveRecently()).toBe(true);
}); });
@@ -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. Please see LICENSE files in the repository root for full details.
*/ */
import { WorkerManager } from "../../src/WorkerManager"; import { vi, describe, it, expect } from "vitest";
import { WorkerManager } from "./WorkerManager";
describe("WorkerManager", () => { describe("WorkerManager", () => {
it("should generate consecutive sequence numbers for each call", () => { it("should generate consecutive sequence numbers for each call", () => {
const postMessage = jest.fn(); const postMessage = vi.fn();
const manager = new WorkerManager({ postMessage } as unknown as Worker); const manager = new WorkerManager({ postMessage } as unknown as Worker);
manager.call({ data: "One" }); manager.call({ data: "One" });
@@ -27,7 +29,7 @@ describe("WorkerManager", () => {
}); });
it("should support resolving out of order", async () => { it("should support resolving out of order", async () => {
const postMessage = jest.fn(); const postMessage = vi.fn();
const worker = { postMessage } as unknown as Worker; const worker = { postMessage } as unknown as Worker;
const manager = new WorkerManager(worker); const manager = new WorkerManager(worker);
@@ -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. Please see LICENSE files in the repository root for full details.
*/ */
import { looksValid } from "../../src/email"; import { describe, it, expect } from "vitest";
import { looksValid } from "./email";
describe("looksValid", () => { describe("looksValid", () => {
it.each([ it.each([
+18
View File
@@ -0,0 +1,18 @@
/*
Copyright 2026 Element Creations Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { beforeEach } from "vitest";
import fetchMock, { manageFetchMockGlobally } from "@fetch-mock/vitest";
manageFetchMockGlobally();
beforeEach(() => {
// set up fetch API mock
fetchMock.hardReset();
fetchMock.catch(404);
fetchMock.mockGlobal();
});
@@ -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. Please see LICENSE files in the repository root for full details.
*/ */
import { lerp } from "../../../src/utils/AnimationUtils"; import { describe, it, expect } from "vitest";
import { lerp } from "./AnimationUtils";
describe("lerp", () => { describe("lerp", () => {
it("correctly interpolates", () => { it("correctly interpolates", () => {
@@ -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. Please see LICENSE files in the repository root for full details.
*/ */
import { FixedRollingArray } from "../../../src/utils/FixedRollingArray"; import { describe, it, expect } from "vitest";
import { FixedRollingArray } from "./FixedRollingArray";
describe("FixedRollingArray", () => { describe("FixedRollingArray", () => {
it("should seed the array with the given value", () => { it("should seed the array with the given value", () => {
@@ -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. Please see LICENSE files in the repository root for full details.
*/ */
import fs from "fs"; import { describe, it, expect } from "vitest";
import path from "path"; import fs from "node:fs";
import path from "node:path";
import { blobIsAnimated, mayBeAnimated } from "../../src/utils/Image"; import { blobIsAnimated, mayBeAnimated } from "./Image";
const imagesDir = path.resolve(__dirname, "../../test/unit-tests/images");
describe("Image", () => { describe("Image", () => {
describe("mayBeAnimated", () => { describe("mayBeAnimated", () => {
@@ -32,28 +35,28 @@ describe("Image", () => {
describe("blobIsAnimated", () => { describe("blobIsAnimated", () => {
it("Animated GIF", async () => { it("Animated GIF", async () => {
const img = new Blob([fs.readFileSync(path.resolve(__dirname, "images", "animated-logo.gif")).slice()], { const img = new Blob([fs.readFileSync(path.resolve(imagesDir, "animated-logo.gif")).slice()], {
type: "image/gif", type: "image/gif",
}); });
expect(await blobIsAnimated(img)).toBeTruthy(); expect(await blobIsAnimated(img)).toBeTruthy();
}); });
it("Static GIF", async () => { it("Static GIF", async () => {
const img = new Blob([fs.readFileSync(path.resolve(__dirname, "images", "static-logo.gif")).slice()], { const img = new Blob([fs.readFileSync(path.resolve(imagesDir, "static-logo.gif")).slice()], {
type: "image/gif", type: "image/gif",
}); });
expect(await blobIsAnimated(img)).toBeFalsy(); expect(await blobIsAnimated(img)).toBeFalsy();
}); });
it("Animated WEBP", async () => { it("Animated WEBP", async () => {
const img = new Blob([fs.readFileSync(path.resolve(__dirname, "images", "animated-logo.webp")).slice()], { const img = new Blob([fs.readFileSync(path.resolve(imagesDir, "animated-logo.webp")).slice()], {
type: "image/webp", type: "image/webp",
}); });
expect(await blobIsAnimated(img)).toBeTruthy(); expect(await blobIsAnimated(img)).toBeTruthy();
}); });
it("Static WEBP", async () => { it("Static WEBP", async () => {
const img = new Blob([fs.readFileSync(path.resolve(__dirname, "images", "static-logo.webp")).slice()], { const img = new Blob([fs.readFileSync(path.resolve(imagesDir, "static-logo.webp")).slice()], {
type: "image/webp", type: "image/webp",
}); });
expect(await blobIsAnimated(img)).toBeFalsy(); expect(await blobIsAnimated(img)).toBeFalsy();
@@ -61,14 +64,14 @@ describe("Image", () => {
it("Static WEBP in extended file format", async () => { it("Static WEBP in extended file format", async () => {
const img = new Blob( const img = new Blob(
[fs.readFileSync(path.resolve(__dirname, "images", "static-logo-extended-file-format.webp")).slice()], [fs.readFileSync(path.resolve(imagesDir, "static-logo-extended-file-format.webp")).slice()],
{ type: "image/webp" }, { type: "image/webp" },
); );
expect(await blobIsAnimated(img)).toBeFalsy(); expect(await blobIsAnimated(img)).toBeFalsy();
}); });
it("Animated PNG", async () => { it("Animated PNG", async () => {
const img = new Blob([fs.readFileSync(path.resolve(__dirname, "images", "animated-logo.apng")).slice()]); const img = new Blob([fs.readFileSync(path.resolve(imagesDir, "animated-logo.apng")).slice()]);
const pngBlob = img.slice(0, img.size, "image/png"); const pngBlob = img.slice(0, img.size, "image/png");
const apngBlob = img.slice(0, img.size, "image/apng"); const apngBlob = img.slice(0, img.size, "image/apng");
expect(await blobIsAnimated(pngBlob)).toBeTruthy(); expect(await blobIsAnimated(pngBlob)).toBeTruthy();
@@ -76,7 +79,7 @@ describe("Image", () => {
}); });
it("Static PNG", async () => { it("Static PNG", async () => {
const img = new Blob([fs.readFileSync(path.resolve(__dirname, "images", "static-logo.png")).slice()]); const img = new Blob([fs.readFileSync(path.resolve(imagesDir, "static-logo.png")).slice()]);
const pngBlob = img.slice(0, img.size, "image/png"); const pngBlob = img.slice(0, img.size, "image/png");
const apngBlob = img.slice(0, img.size, "image/apng"); const apngBlob = img.slice(0, img.size, "image/apng");
expect(await blobIsAnimated(pngBlob)).toBeFalsy(); expect(await blobIsAnimated(pngBlob)).toBeFalsy();
@@ -6,21 +6,18 @@ 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. Please see LICENSE files in the repository root for full details.
*/ */
import { vi, describe, it, expect, beforeEach } from "vitest";
import { logger } from "matrix-js-sdk/src/logger"; import { logger } from "matrix-js-sdk/src/logger";
import { LruCache } from "../../../src/utils/LruCache"; import { LruCache } from "./LruCache";
describe("LruCache", () => { describe("LruCache", () => {
it("when creating a cache with negative capacity it should raise an error", () => { it("when creating a cache with negative capacity it should raise an error", () => {
expect(() => { expect(() => new LruCache(-23)).toThrow("Cache capacity must be at least 1");
new LruCache(-23);
}).toThrow("Cache capacity must be at least 1");
}); });
it("when creating a cache with 0 capacity it should raise an error", () => { it("when creating a cache with 0 capacity it should raise an error", () => {
expect(() => { expect(() => new LruCache(0)).toThrow("Cache capacity must be at least 1");
new LruCache(0);
}).toThrow("Cache capacity must be at least 1");
}); });
describe("when there is a cache with a capacity of 3", () => { describe("when there is a cache with a capacity of 3", () => {
@@ -72,7 +69,7 @@ describe("LruCache", () => {
}); });
it("when an error occurs while setting an item the cache should be cleard", () => { it("when an error occurs while setting an item the cache should be cleard", () => {
jest.spyOn(logger, "warn"); vi.spyOn(logger, "warn");
const err = new Error("Something weng wrong :("); const err = new Error("Something weng wrong :(");
// @ts-ignore // @ts-ignore
@@ -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. Please see LICENSE files in the repository root for full details.
*/ */
import { doesRoomVersionSupport, PreferredRoomVersions } from "../../src/utils/PreferredRoomVersions"; import { describe, it, expect } from "vitest";
import { doesRoomVersionSupport, PreferredRoomVersions } from "./PreferredRoomVersions";
describe("doesRoomVersionSupport", () => { describe("doesRoomVersionSupport", () => {
it("should detect unstable as unsupported", () => { it("should detect unstable as unsupported", () => {
@@ -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. Please see LICENSE files in the repository root for full details.
*/ */
import { SnakedObject, snakeToCamel } from "../../../src/utils/SnakedObject"; import { describe, it, expect } from "vitest";
import { SnakedObject, snakeToCamel } from "./SnakedObject";
describe("snakeToCamel", () => { describe("snakeToCamel", () => {
it("should convert snake_case to camelCase in simple scenarios", () => { it("should convert snake_case to camelCase in simple scenarios", () => {
@@ -6,7 +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. Please see LICENSE files in the repository root for full details.
*/ */
import { abbreviateUrl, parseUrl, unabbreviateUrl } from "../../../src/utils/UrlUtils"; // @vitest-environment happy-dom
import { describe, it, expect } from "vitest";
import { abbreviateUrl, parseUrl, unabbreviateUrl } from "./UrlUtils";
describe("abbreviateUrl", () => { describe("abbreviateUrl", () => {
it("should return empty string if passed falsey", () => { it("should return empty string if passed falsey", () => {
@@ -6,6 +6,8 @@ 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. Please see LICENSE files in the repository root for full details.
*/ */
import { vi, describe, it, expect } from "vitest";
import { import {
arrayDiff, arrayDiff,
arrayFastClone, arrayFastClone,
@@ -25,7 +27,7 @@ import {
asyncSome, asyncSome,
asyncSomeParallel, asyncSomeParallel,
asyncFilter, asyncFilter,
} from "../../../src/utils/arrays"; } from "./arrays";
type TestParams = { input: number[]; output: number[] }; type TestParams = { input: number[]; output: number[] };
type TestCase = [string, TestParams]; type TestCase = [string, TestParams];
@@ -412,11 +414,11 @@ describe("arrays", () => {
describe("asyncEvery", () => { describe("asyncEvery", () => {
it("when called with an empty array, it should return true", async () => { it("when called with an empty array, it should return true", async () => {
expect(await asyncEvery([], jest.fn().mockResolvedValue(true))).toBe(true); expect(await asyncEvery([], vi.fn().mockResolvedValue(true))).toBe(true);
}); });
it("when called with some items and the predicate resolves to true for all of them, it should return true", async () => { it("when called with some items and the predicate resolves to true for all of them, it should return true", async () => {
const predicate = jest.fn().mockResolvedValue(true); const predicate = vi.fn().mockResolvedValue(true);
expect(await asyncEvery([1, 2, 3], predicate)).toBe(true); expect(await asyncEvery([1, 2, 3], predicate)).toBe(true);
expect(predicate).toHaveBeenCalledTimes(3); expect(predicate).toHaveBeenCalledTimes(3);
expect(predicate).toHaveBeenCalledWith(1); expect(predicate).toHaveBeenCalledWith(1);
@@ -425,14 +427,14 @@ describe("arrays", () => {
}); });
it("when called with some items and the predicate resolves to false for all of them, it should return false", async () => { it("when called with some items and the predicate resolves to false for all of them, it should return false", async () => {
const predicate = jest.fn().mockResolvedValue(false); const predicate = vi.fn().mockResolvedValue(false);
expect(await asyncEvery([1, 2, 3], predicate)).toBe(false); expect(await asyncEvery([1, 2, 3], predicate)).toBe(false);
expect(predicate).toHaveBeenCalledTimes(1); expect(predicate).toHaveBeenCalledTimes(1);
expect(predicate).toHaveBeenCalledWith(1); expect(predicate).toHaveBeenCalledWith(1);
}); });
it("when called with some items and the predicate resolves to false for one of them, it should return false", async () => { it("when called with some items and the predicate resolves to false for one of them, it should return false", async () => {
const predicate = jest.fn().mockResolvedValueOnce(true).mockResolvedValueOnce(false); const predicate = vi.fn().mockResolvedValueOnce(true).mockResolvedValueOnce(false);
expect(await asyncEvery([1, 2, 3], predicate)).toBe(false); expect(await asyncEvery([1, 2, 3], predicate)).toBe(false);
expect(predicate).toHaveBeenCalledTimes(2); expect(predicate).toHaveBeenCalledTimes(2);
expect(predicate).toHaveBeenCalledWith(1); expect(predicate).toHaveBeenCalledWith(1);
@@ -442,11 +444,11 @@ describe("arrays", () => {
describe("asyncSome", () => { describe("asyncSome", () => {
it("when called with an empty array, it should return false", async () => { it("when called with an empty array, it should return false", async () => {
expect(await asyncSome([], jest.fn().mockResolvedValue(true))).toBe(false); expect(await asyncSome([], vi.fn().mockResolvedValue(true))).toBe(false);
}); });
it("when called with some items and the predicate resolves to false for all of them, it should return false", async () => { it("when called with some items and the predicate resolves to false for all of them, it should return false", async () => {
const predicate = jest.fn().mockResolvedValue(false); const predicate = vi.fn().mockResolvedValue(false);
expect(await asyncSome([1, 2, 3], predicate)).toBe(false); expect(await asyncSome([1, 2, 3], predicate)).toBe(false);
expect(predicate).toHaveBeenCalledTimes(3); expect(predicate).toHaveBeenCalledTimes(3);
expect(predicate).toHaveBeenCalledWith(1); expect(predicate).toHaveBeenCalledWith(1);
@@ -455,7 +457,7 @@ describe("arrays", () => {
}); });
it("when called with some items and the predicate resolves to true, it should short-circuit and return true", async () => { it("when called with some items and the predicate resolves to true, it should short-circuit and return true", async () => {
const predicate = jest.fn().mockResolvedValueOnce(false).mockResolvedValueOnce(true); const predicate = vi.fn().mockResolvedValueOnce(false).mockResolvedValueOnce(true);
expect(await asyncSome([1, 2, 3], predicate)).toBe(true); expect(await asyncSome([1, 2, 3], predicate)).toBe(true);
expect(predicate).toHaveBeenCalledTimes(2); expect(predicate).toHaveBeenCalledTimes(2);
expect(predicate).toHaveBeenCalledWith(1); expect(predicate).toHaveBeenCalledWith(1);
@@ -465,30 +467,30 @@ describe("arrays", () => {
describe("asyncSomeParallel", () => { describe("asyncSomeParallel", () => {
it("when called with an empty array, it should return false", async () => { it("when called with an empty array, it should return false", async () => {
expect(await asyncSomeParallel([], jest.fn().mockResolvedValue(true))).toBe(false); expect(await asyncSomeParallel([], vi.fn().mockResolvedValue(true))).toBe(false);
}); });
it("when all the predicates return false", async () => { it("when all the predicates return false", async () => {
expect(await asyncSomeParallel([1, 2, 3], jest.fn().mockResolvedValue(false))).toBe(false); expect(await asyncSomeParallel([1, 2, 3], vi.fn().mockResolvedValue(false))).toBe(false);
}); });
it("when all the predicates return true", async () => { it("when all the predicates return true", async () => {
expect(await asyncSomeParallel([1, 2, 3], jest.fn().mockResolvedValue(true))).toBe(true); expect(await asyncSomeParallel([1, 2, 3], vi.fn().mockResolvedValue(true))).toBe(true);
}); });
it("when one of the predicate return true", async () => { it("when one of the predicate return true", async () => {
const predicate = jest.fn().mockImplementation((value) => Promise.resolve(value === 2)); const predicate = vi.fn().mockImplementation((value) => Promise.resolve(value === 2));
expect(await asyncSomeParallel([1, 2, 3], predicate)).toBe(true); expect(await asyncSomeParallel([1, 2, 3], predicate)).toBe(true);
}); });
}); });
describe("asyncFilter", () => { describe("asyncFilter", () => {
it("when called with an empty array, it should return an empty array", async () => { it("when called with an empty array, it should return an empty array", async () => {
expect(await asyncFilter([], jest.fn().mockResolvedValue(true))).toEqual([]); expect(await asyncFilter([], vi.fn().mockResolvedValue(true))).toEqual([]);
}); });
it("should filter the content", async () => { it("should filter the content", async () => {
const predicate = jest.fn().mockImplementation((value) => Promise.resolve(value === 2)); const predicate = vi.fn().mockImplementation((value) => Promise.resolve(value === 2));
expect(await asyncFilter([1, 2, 3], predicate)).toEqual([2]); expect(await asyncFilter([1, 2, 3], predicate)).toEqual([2]);
}); });
}); });
@@ -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. Please see LICENSE files in the repository root for full details.
*/ */
import { textToHtmlRainbow } from "../../../src/utils/colour"; import { describe, it, expect } from "vitest";
import { textToHtmlRainbow } from "./colour";
describe("textToHtmlRainbow", () => { describe("textToHtmlRainbow", () => {
it("correctly transform text to html without splitting the emoji in two", () => { it("correctly transform text to html without splitting the emoji in two", () => {
@@ -6,16 +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. Please see LICENSE files in the repository root for full details.
*/ */
import { vi, describe, it, expect, beforeEach, type Mock } from "vitest";
import { type ClientEvent, type ClientEventHandlerMap, SyncState } from "matrix-js-sdk/src/matrix"; import { type ClientEvent, type ClientEventHandlerMap, SyncState } from "matrix-js-sdk/src/matrix";
import { createReconnectedListener } from "../../../src/utils/connection"; import { createReconnectedListener } from "./connection";
describe("createReconnectedListener", () => { describe("createReconnectedListener", () => {
let reconnectedListener: ClientEventHandlerMap[ClientEvent.Sync]; let reconnectedListener: ClientEventHandlerMap[ClientEvent.Sync];
let onReconnect: jest.Mock; let onReconnect: Mock;
beforeEach(() => { beforeEach(() => {
onReconnect = jest.fn(); onReconnect = vi.fn();
reconnectedListener = createReconnectedListener(onReconnect); reconnectedListener = createReconnectedListener(onReconnect);
}); });
@@ -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. Please see LICENSE files in the repository root for full details.
*/ */
import { getEnumValues, isEnumValue } from "../../../src/utils/enums"; import { describe, it, expect } from "vitest";
import { getEnumValues, isEnumValue } from "./enums";
enum TestStringEnum { enum TestStringEnum {
First = "__first__", First = "__first__",
@@ -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. Please see LICENSE files in the repository root for full details.
*/ */
import { iterableDiff, iterableIntersection } from "../../../src/utils/iterables"; import { describe, it, expect } from "vitest";
import { iterableDiff, iterableIntersection } from "./iterables";
describe("iterables", () => { describe("iterables", () => {
describe("iterableIntersection", () => { describe("iterableIntersection", () => {
@@ -5,7 +5,9 @@
* Please see LICENSE files in the repository root for full details. * Please see LICENSE files in the repository root for full details.
*/ */
import { keepIfSame } from "../../../src/utils/keepIfSame"; import { describe, it, expect } from "vitest";
import { keepIfSame } from "./keepIfSame";
describe("keepIfSame", () => { describe("keepIfSame", () => {
it("returns the next value if the current and next values are not deeply equal", () => { it("returns the next value if the current and next values are not deeply equal", () => {
@@ -6,6 +6,8 @@ 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. Please see LICENSE files in the repository root for full details.
*/ */
import { describe, it, expect } from "vitest";
import { import {
objectClone, objectClone,
objectDiff, objectDiff,
@@ -14,7 +16,7 @@ import {
objectKeyChanges, objectKeyChanges,
objectShallowClone, objectShallowClone,
objectWithOnly, objectWithOnly,
} from "../../../src/utils/objects"; } from "./objects";
describe("objects", () => { describe("objects", () => {
describe("objectExcluding", () => { describe("objectExcluding", () => {
@@ -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. Please see LICENSE files in the repository root for full details.
*/ */
// @vitest-environment happy-dom
import { vi, describe, it, expect, beforeEach } from "vitest";
import { type IdTokenClaims } from "oidc-client-ts"; import { type IdTokenClaims } from "oidc-client-ts";
import { decodeIdToken } from "matrix-js-sdk/src/matrix"; import { decodeIdToken } from "matrix-js-sdk/src/matrix";
import { mocked } from "jest-mock";
import { import {
getStoredOidcClientId, getStoredOidcClientId,
@@ -16,13 +18,13 @@ import {
getStoredOidcIdTokenClaims, getStoredOidcIdTokenClaims,
getStoredOidcTokenIssuer, getStoredOidcTokenIssuer,
persistOidcAuthenticatedSettings, persistOidcAuthenticatedSettings,
} from "../../../../src/utils/oidc/persistOidcSettings"; } from "./persistOidcSettings";
jest.mock("matrix-js-sdk/src/matrix"); vi.mock("matrix-js-sdk/src/matrix");
describe("persist OIDC settings", () => { describe("persist OIDC settings", () => {
jest.spyOn(Storage.prototype, "getItem"); vi.spyOn(localStorage, "getItem");
jest.spyOn(Storage.prototype, "setItem"); vi.spyOn(localStorage, "setItem");
beforeEach(() => { beforeEach(() => {
localStorage.clear(); localStorage.clear();
@@ -101,7 +103,7 @@ describe("persist OIDC settings", () => {
it("should return claims extracted from id_token in localStorage", () => { it("should return claims extracted from id_token in localStorage", () => {
localStorage.setItem("mx_oidc_id_token", idToken); localStorage.setItem("mx_oidc_id_token", idToken);
mocked(decodeIdToken).mockReturnValue(idTokenClaims); vi.mocked(decodeIdToken).mockReturnValue(idTokenClaims);
expect(getStoredOidcIdTokenClaims()).toEqual(idTokenClaims); expect(getStoredOidcIdTokenClaims()).toEqual(idTokenClaims);
expect(decodeIdToken).toHaveBeenCalledWith(idToken); expect(decodeIdToken).toHaveBeenCalledWith(idToken);
expect(localStorage.getItem).toHaveBeenCalledWith("mx_oidc_id_token_claims"); expect(localStorage.getItem).toHaveBeenCalledWith("mx_oidc_id_token_claims");
@@ -5,7 +5,9 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details. Please see LICENSE files in the repository root for full details.
*/ */
import { getManageDeviceUrl } from "../../../../src/utils/oidc/urls"; import { describe, it, expect } from "vitest";
import { getManageDeviceUrl } from "./urls";
describe("OIDC urls", () => { describe("OIDC urls", () => {
const accountManagementEndpoint = "https://auth.com/manage"; const accountManagementEndpoint = "https://auth.com/manage";
@@ -5,11 +5,13 @@
* Please see LICENSE files in the repository root for full details. * Please see LICENSE files in the repository root for full details.
*/ */
import { batch } from "../../../src/utils/promise.ts"; import { vi, describe, it, expect, afterEach } from "vitest";
import { batch } from "./promise.ts";
describe("promise.ts", () => { describe("promise.ts", () => {
describe("batch", () => { describe("batch", () => {
afterEach(() => jest.useRealTimers()); afterEach(() => vi.useRealTimers());
it("should batch promises into groups of a given size", async () => { it("should batch promises into groups of a given size", async () => {
const promises = [() => Promise.resolve(1), () => Promise.resolve(2), () => Promise.resolve(3)]; const promises = [() => Promise.resolve(1), () => Promise.resolve(2), () => Promise.resolve(3)];
@@ -19,7 +21,7 @@ describe("promise.ts", () => {
}); });
it("should wait for the current batch to finish to request the next one", async () => { it("should wait for the current batch to finish to request the next one", async () => {
jest.useFakeTimers(); vi.useFakeTimers();
let promise1Called = false; let promise1Called = false;
const promise1 = () => const promise1 = () =>
@@ -49,7 +51,7 @@ describe("promise.ts", () => {
expect(promise2Called).toBe(true); expect(promise2Called).toBe(true);
expect(promise3Called).toBe(false); expect(promise3Called).toBe(false);
jest.advanceTimersByTime(11); vi.advanceTimersByTime(11);
expect(await batchPromise).toEqual([1, 2, 3]); expect(await batchPromise).toEqual([1, 2, 3]);
}); });
}); });
@@ -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. Please see LICENSE files in the repository root for full details.
*/ */
import { setHasDiff } from "../../../src/utils/sets"; import { describe, it, expect } from "vitest";
import { setHasDiff } from "./sets";
describe("sets", () => { describe("sets", () => {
describe("setHasDiff", () => { describe("setHasDiff", () => {
@@ -6,10 +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. Please see LICENSE files in the repository root for full details.
*/ */
import { describe, it, expect } from "vitest";
import { sortBy } from "lodash"; import { sortBy } from "lodash";
import { averageBetweenStrings, DEFAULT_ALPHABET } from "matrix-js-sdk/src/utils"; import { averageBetweenStrings, DEFAULT_ALPHABET } from "matrix-js-sdk/src/utils";
import { midPointsBetweenStrings, reorderLexicographically } from "../../../src/utils/stringOrderField"; import { midPointsBetweenStrings, reorderLexicographically } from "./stringOrderField";
const moveLexicographicallyTest = ( const moveLexicographicallyTest = (
orders: Array<string | undefined>, orders: Array<string | undefined>,
@@ -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. Please see LICENSE files in the repository root for full details.
*/ */
import { validateNumberInRange } from "../../../../src/utils/validate"; import { describe, it, expect } from "vitest";
import { validateNumberInRange } from "./numberInRange";
describe("validateNumberInRange", () => { describe("validateNumberInRange", () => {
const min = 1; const min = 1;
@@ -6,9 +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. Please see LICENSE files in the repository root for full details.
*/ */
import fetchMock from "@fetch-mock/jest"; // @vitest-environment happy-dom
import { getVectorConfig } from "../../../src/vector/getconfig"; import { vi, describe, it, expect, beforeEach, afterAll } from "vitest";
import fetchMock from "@fetch-mock/vitest";
import { getVectorConfig } from "./getconfig";
describe("getVectorConfig()", () => { describe("getVectorConfig()", () => {
const elementDomain = "app.element.io"; const elementDomain = "app.element.io";
@@ -27,13 +30,13 @@ describe("getVectorConfig()", () => {
}); });
// stable value for cachebuster // stable value for cachebuster
jest.spyOn(Date, "now").mockReturnValue(now); vi.spyOn(Date, "now").mockReturnValue(now);
jest.clearAllMocks(); vi.clearAllMocks();
fetchMock.removeRoutes(); fetchMock.removeRoutes();
}); });
afterAll(() => { afterAll(() => {
jest.spyOn(Date, "now").mockRestore(); vi.spyOn(Date, "now").mockRestore();
}); });
it("requests specific config for document domain", async () => { it("requests specific config for document domain", async () => {
@@ -6,16 +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. Please see LICENSE files in the repository root for full details.
*/ */
import { getInitialScreenAfterLogin, init, onNewScreen } from "../../../src/vector/routing"; // @vitest-environment happy-dom
import type MatrixChat from "../../../src/components/structures/MatrixChat.tsx";
import { vi, describe, it, expect, beforeEach, afterAll } from "vitest";
import { getInitialScreenAfterLogin, init, onNewScreen } from "./routing";
import type MatrixChat from "../components/structures/MatrixChat.tsx";
describe("onNewScreen", () => { describe("onNewScreen", () => {
it("should replace history if stripping via fields", () => { it("should replace history if stripping via fields", () => {
Object.defineProperty(window, "location", { Object.defineProperty(window, "location", {
value: { value: {
hash: "#/room/!room:server?via=abc", hash: "#/room/!room:server?via=abc",
replace: jest.fn(), replace: vi.fn(),
assign: jest.fn(), assign: vi.fn(),
}, },
writable: true, writable: true,
}); });
@@ -28,8 +32,8 @@ describe("onNewScreen", () => {
Object.defineProperty(window, "location", { Object.defineProperty(window, "location", {
value: { value: {
hash: "#/room/!room1:server?via=abc", hash: "#/room/!room1:server?via=abc",
replace: jest.fn(), replace: vi.fn(),
assign: jest.fn(), assign: vi.fn(),
}, },
writable: true, writable: true,
}); });
@@ -41,8 +45,8 @@ describe("onNewScreen", () => {
describe("getInitialScreenAfterLogin", () => { describe("getInitialScreenAfterLogin", () => {
beforeEach(() => { beforeEach(() => {
jest.spyOn(sessionStorage.__proto__, "getItem").mockClear().mockReturnValue(null); vi.spyOn(sessionStorage, "getItem").mockClear().mockReturnValue(null);
jest.spyOn(sessionStorage.__proto__, "setItem").mockClear(); vi.spyOn(sessionStorage, "setItem").mockClear();
}); });
const makeMockLocation = (hash = "") => { const makeMockLocation = (hash = "") => {
@@ -65,7 +69,7 @@ describe("getInitialScreenAfterLogin", () => {
const screen = { const screen = {
screen: "/room/!test", screen: "/room/!test",
}; };
jest.spyOn(sessionStorage.__proto__, "getItem").mockReturnValue(JSON.stringify(screen)); vi.spyOn(sessionStorage, "getItem").mockReturnValue(JSON.stringify(screen));
expect(getInitialScreenAfterLogin(makeMockLocation())).toEqual(screen); expect(getInitialScreenAfterLogin(makeMockLocation())).toEqual(screen);
}); });
}); });
@@ -111,7 +115,7 @@ describe("init", () => {
}); });
window.matrixChat = { window.matrixChat = {
showScreen: jest.fn(), showScreen: vi.fn(),
} as unknown as MatrixChat; } as unknown as MatrixChat;
init(); init();
@@ -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. Please see LICENSE files in the repository root for full details.
*/ */
import { parseAppUrl, parseQsFromFragment, searchParamsToQueryDict } from "../../../src/vector/url_utils"; import { describe, it, expect } from "vitest";
import { parseAppUrl, parseQsFromFragment, searchParamsToQueryDict } from "./url_utils";
// @ts-ignore // @ts-ignore
const location: Location = { const location: Location = {
+18
View File
@@ -0,0 +1,18 @@
/*
Copyright 2026 Element Creations Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { defineProject } from "vitest/config";
export default defineProject({
test: {
include: ["src/**/*.test.{ts,tsx}"],
environment: "node",
pool: "threads",
globals: false,
setupFiles: ["src/test/setupTests.ts"],
},
});
+1 -1
View File
@@ -23,7 +23,7 @@
"docs:dev": "vitepress dev docs", "docs:dev": "vitepress dev docs",
"docs:build": "vitepress build docs", "docs:build": "vitepress build docs",
"docs:preview": "vitepress preview docs", "docs:preview": "vitepress preview docs",
"test:unit": "vitest run", "test:unit": "nx run-many -t test:unit:prepare -p && vitest run",
"coverage:diff": "diff-cover --config-file diff-cover.toml apps/web/coverage/lcov.info packages/shared-components/coverage/lcov.info coverage/lcov.info" "coverage:diff": "diff-cover --config-file diff-cover.toml apps/web/coverage/lcov.info packages/shared-components/coverage/lcov.info coverage/lcov.info"
}, },
"devDependencies": { "devDependencies": {
+261 -215
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -117,7 +117,7 @@ overrides:
caniuse-lite: 1.0.30001793 caniuse-lite: 1.0.30001793
markdown-it: 14.1.1 markdown-it: 14.1.1
matrix-widget-api: "^1.17.0" matrix-widget-api: "^1.17.0"
"@types/node": 22.13.14 "@types/node": ts6.0
config-file-ts: 0.2.8-rc1 config-file-ts: 0.2.8-rc1
node-abi: 4.31.0 node-abi: 4.31.0
"@types/pg-pool": 2.0.7 "@types/pg-pool": 2.0.7
@@ -139,3 +139,4 @@ minimumReleaseAgeExclude:
- "@matrix-org/*" - "@matrix-org/*"
- "@vector-im/*" - "@vector-im/*"
- "@element-hq/*" - "@element-hq/*"
- "@types/node@25.9.3"
+3 -1
View File
@@ -55,7 +55,9 @@ export default defineConfig({
include: ["{apps,modules,packages}/*/src/**/*.{cts,ts,tsx}"], include: ["{apps,modules,packages}/*/src/**/*.{cts,ts,tsx}"],
exclude: [ exclude: [
// Exclude test files // Exclude test files
"**/*.{stories,test}.tsx", "**/*.{stories,test}.{ts,tsx}",
// Exclude test utilities
"**/src/test/**",
// Exclude type definition files // Exclude type definition files
"**/*.d.ts", "**/*.d.ts",
], ],