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:
@@ -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'
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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([
|
||||||
@@ -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();
|
||||||
|
});
|
||||||
+3
-1
@@ -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", () => {
|
||||||
+3
-1
@@ -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();
|
||||||
+5
-8
@@ -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
|
||||||
+3
-1
@@ -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", () => {
|
||||||
+3
-1
@@ -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", () => {
|
||||||
+5
-1
@@ -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", () => {
|
||||||
+4
-3
@@ -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__",
|
||||||
+3
-1
@@ -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", () => {
|
||||||
+3
-1
@@ -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", () => {
|
||||||
+8
-6
@@ -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");
|
||||||
+3
-1
@@ -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", () => {
|
||||||
+2
-1
@@ -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>,
|
||||||
+3
-1
@@ -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;
|
||||||
+8
-5
@@ -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 () => {
|
||||||
+14
-10
@@ -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();
|
||||||
+3
-1
@@ -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 = {
|
||||||
@@ -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
@@ -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": {
|
||||||
|
|||||||
Generated
+261
-215
File diff suppressed because it is too large
Load Diff
+2
-1
@@ -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
@@ -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",
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user