diff --git a/README.md b/README.md index e3bf79204a0..0fe33f162da 100644 --- a/README.md +++ b/README.md @@ -448,7 +448,7 @@ To build a browser version from scratch when developing: $ yarn build ``` -To run tests (Jest): +To run tests: ``` $ yarn test diff --git a/jest.config.ts b/jest.config.ts deleted file mode 100644 index 1e6833a586d..00000000000 --- a/jest.config.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* Copyright 2023 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import type { Config } from "jest"; -import { env } from "process"; - -const config: Config = { - testEnvironment: "node", - testMatch: ["/spec/**/*.spec.{js,ts}"], - setupFilesAfterEnv: ["/spec/setupTests.ts"], - collectCoverageFrom: ["/src/**/*.{js,ts}"], - coverageReporters: ["text-summary", "lcov"], - testResultsProcessor: "@casualbot/jest-sonar-reporter", - - // Always print out a summary if there are any failing tests. Normally - // a summary is only printed if there are more than 20 test *suites*. - reporters: [["default", { summaryThreshold: 0 }]], -}; - -// if we're running under GHA, enable the GHA reporter -if (env["GITHUB_ACTIONS"] !== undefined) { - const reporters: Config["reporters"] = [ - ["github-actions", { silent: false }], - // as above: always show a summary if there were any failing tests. - ["summary", { summaryThreshold: 0 }], - ]; - - // if we're running against the develop branch, also enable the slow test reporter - if (env["GITHUB_REF"] == "refs/heads/develop") { - reporters.push("/spec/slowReporter.cjs"); - } - config.reporters = reporters; -} - -export default config; diff --git a/knip.ts b/knip.ts index 4709fdbc35c..29d87fd0cf6 100644 --- a/knip.ts +++ b/knip.ts @@ -31,10 +31,6 @@ export default { "husky", // Used in script which only runs in environment with `@octokit/rest` installed "@octokit/rest", - // Used by jest - "jest-environment-jsdom", - "babel-jest", - "ts-node", // Used by `@babel/plugin-transform-runtime` "@babel/runtime", ], diff --git a/package.json b/package.json index c64dc136aab..269363e4225 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,8 @@ "lint:types": "tsc --noEmit", "lint:workflows": "find .github/workflows -type f \\( -iname '*.yaml' -o -iname '*.yml' \\) | xargs -I {} sh -c 'echo \"Linting {}\"; action-validator \"{}\"'", "lint:knip": "knip", - "test": "jest", - "test:watch": "jest --watch", + "test": "vitest", + "test:watch": "vitest --watch", "coverage": "yarn test --coverage" }, "repository": { @@ -79,19 +79,17 @@ "@babel/plugin-transform-runtime": "^7.12.10", "@babel/preset-env": "^7.12.11", "@babel/preset-typescript": "^7.12.7", - "@casualbot/jest-sonar-reporter": "2.2.7", - "@peculiar/webcrypto": "^1.4.5", + "@fetch-mock/vitest": "^0.2.8", "@stylistic/eslint-plugin": "^3.0.0", "@types/bs58": "^4.0.1", "@types/content-type": "^1.1.5", "@types/debug": "^4.1.7", - "@types/jest": "^29.0.0", "@types/node": "18", "@types/sdp-transform": "^2.4.5", "@types/uuid": "10", "@typescript-eslint/eslint-plugin": "^8.0.0", "@typescript-eslint/parser": "^8.0.0", - "babel-jest": "^29.0.0", + "@vitest/ui": "^3.0.5", "babel-plugin-search-and-replace": "^1.1.1", "debug": "^4.3.4", "eslint": "8.57.1", @@ -99,20 +97,15 @@ "eslint-config-prettier": "^10.0.0", "eslint-import-resolver-typescript": "^3.5.1", "eslint-plugin-import": "^2.26.0", - "eslint-plugin-jest": "^28.0.0", "eslint-plugin-jsdoc": "^50.0.0", "eslint-plugin-matrix-org": "^2.0.1", "eslint-plugin-n": "^14.0.0", "eslint-plugin-tsdoc": "^0.4.0", "eslint-plugin-unicorn": "^56.0.0", + "eslint-plugin-vitest": "^0.5.4", "fake-indexeddb": "^5.0.2", - "fetch-mock": "11.1.5", - "fetch-mock-jest": "^1.5.1", "husky": "^9.0.0", - "jest": "^29.0.0", - "jest-environment-jsdom": "^29.0.0", - "jest-localstorage-mock": "^2.4.6", - "jest-mock": "^29.0.0", + "jsdom": "^26.0.0", "knip": "^5.0.0", "lint-staged": "^15.0.2", "matrix-mock-request": "^2.5.0", @@ -124,11 +117,8 @@ "typedoc-plugin-coverage": "^3.0.0", "typedoc-plugin-mdn-links": "^4.0.0", "typedoc-plugin-missing-exports": "^3.0.0", - "typescript": "^5.4.2" - }, - "@casualbot/jest-sonar-reporter": { - "outputDirectory": "coverage", - "outputName": "jest-sonar-report.xml", - "relativePaths": true + "typescript": "^5.4.2", + "vitest": "^3.0.5", + "vitest-sonar-reporter": "^2.0.0" } } diff --git a/sonar-project.properties b/sonar-project.properties index 5cfec433ca3..a2eb7e7a4de 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -11,6 +11,6 @@ sonar.exclusions=docs,examples,git-hooks sonar.typescript.tsconfigPath=./tsconfig.json sonar.javascript.lcov.reportPaths=coverage/lcov.info sonar.coverage.exclusions=spec/**/* -sonar.testExecutionReportPaths=coverage/jest-sonar-report.xml +sonar.testExecutionReportPaths=coverage/sonar-report.xml sonar.lang.patterns.ts=**/*.ts,**/*.tsx diff --git a/spec/TestClient.ts b/spec/TestClient.ts index 66c63064e4d..3f4ca865b14 100644 --- a/spec/TestClient.ts +++ b/spec/TestClient.ts @@ -16,10 +16,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -// `expect` is allowed in helper functions which are called within `test`/`it` blocks -/* eslint-disable jest/no-standalone-expect */ - // load olm before the sdk if possible +import { expect } from "vitest"; + import "./olm-loader"; import MockHttpBackend from "matrix-mock-request"; @@ -39,7 +38,7 @@ import { ISyncResponder } from "./test-utils/SyncResponder"; * Wrapper for a MockStorageApi, MockHttpBackend and MatrixClient * * @deprecated Avoid using this; it is tied too tightly to matrix-mock-request and is generally inconvenient to use. - * Instead, construct a MatrixClient manually, use fetch-mock-jest to intercept the HTTP requests, and + * Instead, construct a MatrixClient manually, use fetch-mock to intercept the HTTP requests, and * use things like {@link E2EKeyReceiver} and {@link SyncResponder} to manage the requests. */ export class TestClient implements IE2EKeyReceiver, ISyncResponder { diff --git a/spec/integ/crypto/cross-signing.spec.ts b/spec/integ/crypto/cross-signing.spec.ts index 95b0f756e0a..dca0d2d9934 100644 --- a/spec/integ/crypto/cross-signing.spec.ts +++ b/spec/integ/crypto/cross-signing.spec.ts @@ -14,7 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -import fetchMock from "fetch-mock-jest"; +import { afterEach, beforeEach, describe, expect, it, test, vi } from "vitest"; + +import fetchMock from "@fetch-mock/vitest"; import "fake-indexeddb/auto"; import { IDBFactory } from "fake-indexeddb"; @@ -85,7 +87,6 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("cross-signing (%s)", (backend: s async () => { // anything that we don't have a specific matcher for silently returns a 404 fetchMock.catch(404); - fetchMock.config.warnOnFallback = false; const homeserverUrl = "https://alice-server.com"; aliceClient = createClient({ @@ -139,8 +140,8 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("cross-signing (%s)", (backend: s await bootstrapCrossSigning(authDict); // check the cross-signing keys upload - expect(fetchMock.called("upload-keys")).toBeTruthy(); - const [, keysOpts] = fetchMock.lastCall("upload-keys")!; + expect(fetchMock.callHistory.called("upload-keys")).toBeTruthy(); + const [, keysOpts] = fetchMock.callHistory.lastCall("upload-keys")!; const keysBody = JSON.parse(keysOpts!.body as string); expect(keysBody.auth).toEqual(authDict); // check uia dict was passed // there should be a key of each type @@ -153,8 +154,8 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("cross-signing (%s)", (backend: s const sskId = Object.keys(keysBody.self_signing_key.keys)[0]; // check the publish call - expect(fetchMock.called("upload-sigs")).toBeTruthy(); - const [, sigsOpts] = fetchMock.lastCall("upload-sigs")!; + expect(fetchMock.callHistory.called("upload-sigs")).toBeTruthy(); + const [, sigsOpts] = fetchMock.callHistory.lastCall("upload-sigs")!; const body = JSON.parse(sigsOpts!.body as string); // there should be a signature for our device, by our self-signing key. expect(body).toHaveProperty( @@ -227,7 +228,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("cross-signing (%s)", (backend: s await syncPromise(aliceClient); // we expect a request to upload signatures for our device ... - fetchMock.post({ url: "path:/_matrix/client/v3/keys/signatures/upload", name: "upload-sigs" }, {}); + fetchMock.post("path:/_matrix/client/v3/keys/signatures/upload", {}, { name: "upload-sigs" }); // we expect the UserTrustStatusChanged event to be fired after the cross signing keys import const userTrustStatusChangedPromise = new Promise((resolve) => @@ -241,8 +242,8 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("cross-signing (%s)", (backend: s expect(await userTrustStatusChangedPromise).toBe(aliceClient.getUserId()); // Expect the signature to be uploaded - expect(fetchMock.called("upload-sigs")).toBeTruthy(); - const [, sigsOpts] = fetchMock.lastCall("upload-sigs")!; + expect(fetchMock.callHistory.called("upload-sigs")).toBeTruthy(); + const [, sigsOpts] = fetchMock.callHistory.lastCall("upload-sigs")!; const body = JSON.parse(sigsOpts!.body as string); // the device should have a signature with the public self cross signing keys. expect(body).toHaveProperty( @@ -259,7 +260,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("cross-signing (%s)", (backend: s // a second call should do nothing except GET requests fetchMock.mockClear(); await bootstrapCrossSigning(authDict); - const calls = fetchMock.calls((url, opts) => opts.method != "GET"); + const calls = fetchMock.callHistory.calls((callLog) => callLog.request?.method != "GET"); expect(calls.length).toEqual(0); }); @@ -421,17 +422,14 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("cross-signing (%s)", (backend: s return new Promise((resolve) => { fetchMock.post( // legacy crypto uses /unstable/; /v3/ is correct - { - url: new RegExp("/_matrix/client/(unstable|v3)/keys/device_signing/upload"), - name: "upload-keys", - }, - (url, options) => { - const content = JSON.parse(options.body as string); + new RegExp("/_matrix/client/(unstable|v3)/keys/device_signing/upload"), + async (callLog) => { + const content = await callLog.request?.json(); resolve(content); return {}; }, // Override the routes define in `mockSetupCrossSigningRequests` - { overwriteRoutes: true }, + { name: "upload-keys" }, ); }); } @@ -464,7 +462,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("cross-signing (%s)", (backend: s describe("crossSignDevice", () => { beforeEach(async () => { // We want to use fake timers, but the wasm bindings of matrix-sdk-crypto rely on a working `queueMicrotask`. - jest.useFakeTimers({ doNotFake: ["queueMicrotask"] }); + vi.useFakeTimers({ toFake: ["setTimeout", "setInterval", "setImmediate"] }); // make sure that there is another device which we can sign e2eKeyResponder.addDeviceKeys(SIGNED_TEST_DEVICE_DATA); @@ -476,14 +474,14 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("cross-signing (%s)", (backend: s await syncPromise(aliceClient); // Wait for legacy crypto to find the device - await jest.advanceTimersByTimeAsync(10); + await vi.advanceTimersByTimeAsync(10); const devices = await aliceClient.getCrypto()!.getUserDeviceInfo([aliceClient.getSafeUserId()]); expect(devices.get(aliceClient.getSafeUserId())!.has(testData.TEST_DEVICE_ID)).toBeTruthy(); }); afterEach(async () => { - jest.useRealTimers(); + vi.useRealTimers(); }); it("fails for an unknown device", async () => { @@ -498,9 +496,9 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("cross-signing (%s)", (backend: s await aliceClient.getCrypto()!.crossSignDevice(testData.TEST_DEVICE_ID); // check that a sig for the device was uploaded - const calls = fetchMock.calls("upload-sigs"); + const calls = fetchMock.callHistory.calls("upload-sigs"); expect(calls.length).toEqual(1); - const body = JSON.parse(calls[0][1]!.body as string); + const body = await calls[0].request?.json(); const deviceSig = body[aliceClient.getSafeUserId()][testData.TEST_DEVICE_ID]; expect(deviceSig).toHaveProperty("signatures"); }); diff --git a/spec/integ/crypto/crypto.spec.ts b/spec/integ/crypto/crypto.spec.ts index d3f5e20f719..40fd2eccefd 100644 --- a/spec/integ/crypto/crypto.spec.ts +++ b/spec/integ/crypto/crypto.spec.ts @@ -15,8 +15,10 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { afterEach, beforeEach, describe, expect, it, test, vi } from "vitest"; + import anotherjson from "another-json"; -import fetchMock from "fetch-mock-jest"; +import fetchMock from "@fetch-mock/vitest"; import "fake-indexeddb/auto"; import { IDBFactory } from "fake-indexeddb"; import FetchMock from "fetch-mock"; @@ -113,7 +115,7 @@ afterEach(() => { // eslint-disable-next-line no-global-assign indexedDB = new IDBFactory(); - jest.useRealTimers(); + vi.useRealTimers(); }); /** @@ -398,7 +400,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, await aliceClient.stopClient(); // Allow in-flight things to complete before we tear down the test - await jest.runAllTimersAsync(); + await vi.runAllTimersAsync(); fetchMock.mockReset(); }); @@ -474,7 +476,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, describe("Unable to decrypt error codes", function () { beforeEach(() => { - jest.useFakeTimers({ doNotFake: ["queueMicrotask"] }); + vi.useFakeTimers({ doNotFake: ["queueMicrotask"] }); }); it("Decryption fails with UISI error", async () => { @@ -1374,7 +1376,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, // We need to fake the timers to advance the time, but the wasm bindings of matrix-sdk-crypto rely on a // working `queueMicrotask` - jest.useFakeTimers({ doNotFake: ["queueMicrotask"] }); + vi.useFakeTimers({ doNotFake: ["queueMicrotask"] }); const syncResponse = getSyncResponse(["@bob:xyz"]); @@ -1406,7 +1408,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, expect(sessionId).toBeDefined(); // Advance the time by 1h - jest.advanceTimersByTime(oneHourInMs); + vi.advanceTimersByTime(oneHourInMs); // Send a second message to bob and get the encrypted message const [secondEncryptedMessage] = await Promise.all([ @@ -2576,7 +2578,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, describe("key upload request", () => { beforeEach(() => { // We want to use fake timers, but the wasm bindings of matrix-sdk-crypto rely on a working `queueMicrotask`. - jest.useFakeTimers({ doNotFake: ["queueMicrotask"] }); + vi.useFakeTimers({ doNotFake: ["queueMicrotask"] }); }); function awaitKeyUploadRequest(): Promise<{ keysCount: number; fallbackKeysCount: number }> { @@ -2628,7 +2630,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, // Advance local date to 2 minutes // The old crypto only runs the upload every 60 seconds - jest.setSystemTime(Date.now() + 2 * 60 * 1000); + vi.setSystemTime(Date.now() + 2 * 60 * 1000); await syncPromise(aliceClient); @@ -2770,7 +2772,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, it("Get devices from tracked users", async () => { // We want to use fake timers, but the wasm bindings of matrix-sdk-crypto rely on a working `queueMicrotask`. - jest.useFakeTimers({ doNotFake: ["queueMicrotask"] }); + vi.useFakeTimers({ doNotFake: ["queueMicrotask"] }); expectAliceKeyQuery({ device_keys: { "@alice:localhost": {} }, failures: {} }); await startClientAndAwaitFirstSync(); @@ -2782,12 +2784,12 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, // Advance local date to 2 minutes // The old crypto only runs the upload every 60 seconds - jest.setSystemTime(Date.now() + 2 * 60 * 1000); + vi.setSystemTime(Date.now() + 2 * 60 * 1000); await syncPromise(aliceClient); // Old crypto: for alice: run over the `sleep(5)` in `doQueuedQueries` of `DeviceList` - jest.runAllTimers(); + vi.runAllTimers(); // Old crypto: for alice: run the `processQueryResponseForUser` in `doQueuedQueries` of `DeviceList` await flushPromises(); @@ -2795,7 +2797,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, await queryPromise; // Old crypto: for `user`: run over the `sleep(5)` in `doQueuedQueries` of `DeviceList` - jest.runAllTimers(); + vi.runAllTimers(); // Old crypto: for `user`: run the `processQueryResponseForUser` in `doQueuedQueries` of `DeviceList` // It will add `@testing_florian1:matrix.org` devices to the DeviceList await flushPromises(); @@ -2819,7 +2821,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, * Create a fake secret storage key * Async because `bootstrapSecretStorage` expect an async method */ - const createSecretStorageKey = jest.fn().mockResolvedValue({ + const createSecretStorageKey = vi.fn().mockResolvedValue({ keyInfo: {}, // Returning undefined here used to cause a crash privateKey: Uint8Array.of(32, 33), }); @@ -3152,7 +3154,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, describe("Manage Key Backup", () => { beforeEach(async () => { // We want to use fake timers, but the wasm bindings of matrix-sdk-crypto rely on a working `queueMicrotask`. - jest.useFakeTimers({ doNotFake: ["queueMicrotask"] }); + vi.useFakeTimers({ doNotFake: ["queueMicrotask"] }); }); it("Should be able to restore from 4S after bootstrap", async () => { @@ -3187,7 +3189,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, await aliceClient.getCrypto()!.importRoomKeys([newKey]); // The backup loop waits a random amount of time to avoid different clients firing at the same time. - jest.runAllTimers(); + vi.runAllTimers(); const keyBackupData = await awaitKeyUploaded; @@ -3263,7 +3265,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, // Track calls to scheduleAllGroupSessionsForBackup. This is // only relevant on legacy encryption. - const scheduleAllGroupSessionsForBackup = jest.fn(); + const scheduleAllGroupSessionsForBackup = vi.fn(); if (backend === "libolm") { aliceClient.crypto!.backupManager.scheduleAllGroupSessionsForBackup = scheduleAllGroupSessionsForBackup; @@ -3329,7 +3331,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, it("Cross signing keys are available for a tracked user", async () => { // Process Alice keys, old crypto has a sleep(5ms) during the process - await jest.advanceTimersByTimeAsync(5); + await vi.advanceTimersByTimeAsync(5); await flushPromises(); // Alice is the local user and should be tracked ! diff --git a/spec/integ/crypto/device-dehydration.spec.ts b/spec/integ/crypto/device-dehydration.spec.ts index 4bb82bcf59b..aabb802ab7e 100644 --- a/spec/integ/crypto/device-dehydration.spec.ts +++ b/spec/integ/crypto/device-dehydration.spec.ts @@ -14,8 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { describe, expect, it, vi } from "vitest"; + import "fake-indexeddb/auto"; -import fetchMock from "fetch-mock-jest"; +import fetchMock from "@fetch-mock/vitest"; import { ClientEvent, createClient, MatrixClient, MatrixEvent } from "../../../src"; import { CryptoEvent } from "../../../src/crypto-api/index"; @@ -27,7 +29,7 @@ import { emitPromise, EventCounter } from "../../test-utils/test-utils"; describe("Device dehydration", () => { it("should rehydrate and dehydrate a device", async () => { - jest.useFakeTimers({ doNotFake: ["queueMicrotask"] }); + vi.useFakeTimers({ doNotFake: ["queueMicrotask"] }); const matrixClient = createClient({ baseUrl: "http://test.server", @@ -89,7 +91,7 @@ describe("Device dehydration", () => { const dehydrationPromise = new Promise((resolve, reject) => { resolveDehydrationPromise = resolve; }); - jest.advanceTimersByTime(7 * 24 * 60 * 60 * 1000); + vi.advanceTimersByTime(7 * 24 * 60 * 60 * 1000); await dehydrationPromise; expect(dehydrationKeyCachedEventCounter.counter).toEqual(1); @@ -103,7 +105,7 @@ describe("Device dehydration", () => { device_id: dehydratedDeviceBody.device_id, device_data: dehydratedDeviceBody.device_data, }); - const eventsResponse = jest.fn((url, opts) => { + const eventsResponse = vi.fn((url, opts) => { // rehydrating should make two calls to the /events endpoint. // The first time will return a single event, and the second // time will return no events (which will signal to the @@ -141,7 +143,7 @@ describe("Device dehydration", () => { }, }); const rotationErrorEventPromise = emitPromise(matrixClient, CryptoEvent.DehydratedDeviceRotationError); - jest.advanceTimersByTime(7 * 24 * 60 * 60 * 1000); + vi.advanceTimersByTime(7 * 24 * 60 * 60 * 1000); await rotationErrorEventPromise; // Restart dehydration, but return an error for GET /dehydrated_device so that rehydration fails. diff --git a/spec/integ/crypto/megolm-backup.spec.ts b/spec/integ/crypto/megolm-backup.spec.ts index eff0ff567e1..c50350f2554 100644 --- a/spec/integ/crypto/megolm-backup.spec.ts +++ b/spec/integ/crypto/megolm-backup.spec.ts @@ -14,10 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -import fetchMock from "fetch-mock-jest"; +import { afterEach, beforeEach, describe, expect, it, test, vi, Mocked } from "vitest"; + +import fetchMock from "@fetch-mock/vitest"; import "fake-indexeddb/auto"; import { IDBFactory } from "fake-indexeddb"; -import { Mocked } from "jest-mock"; import { createClient, @@ -77,40 +78,34 @@ function mockUploadEmitter( expectedVersion: string, ): TypedEventEmitter { const emitter = new TypedEventEmitter(); - fetchMock.put( - "path:/_matrix/client/v3/room_keys/keys", - (url, request) => { - const version = new URLSearchParams(new URL(url).search).get("version"); - if (version != expectedVersion) { - return { - status: 403, - body: { - current_version: expectedVersion, - errcode: "M_WRONG_ROOM_KEYS_VERSION", - error: "Wrong backup version.", - }, - }; - } - const uploadPayload: KeyBackup = JSON.parse((request.body as string) ?? "{}"); - let count = 0; - for (const [roomId, value] of Object.entries(uploadPayload.rooms)) { - for (const sessionId of Object.keys(value.sessions)) { - emitter.emit(MockKeyUploadEvent.KeyUploaded, roomId, sessionId, version); - count++; - } - } + fetchMock.put("path:/_matrix/client/v3/room_keys/keys", (url, request) => { + const version = new URLSearchParams(new URL(url).search).get("version"); + if (version != expectedVersion) { return { - status: 200, + status: 403, body: { - count: count, - etag: "abcdefg", + current_version: expectedVersion, + errcode: "M_WRONG_ROOM_KEYS_VERSION", + error: "Wrong backup version.", }, }; - }, - { - overwriteRoutes: true, - }, - ); + } + const uploadPayload: KeyBackup = JSON.parse((request.body as string) ?? "{}"); + let count = 0; + for (const [roomId, value] of Object.entries(uploadPayload.rooms)) { + for (const sessionId of Object.keys(value.sessions)) { + emitter.emit(MockKeyUploadEvent.KeyUploaded, roomId, sessionId, version); + count++; + } + } + return { + status: 200, + body: { + count: count, + etag: "abcdefg", + }, + }; + }); return emitter; } @@ -133,7 +128,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe beforeEach(async () => { // We want to use fake timers, but the wasm bindings of matrix-sdk-crypto rely on a working `queueMicrotask`. - jest.useFakeTimers({ doNotFake: ["queueMicrotask"] }); + vi.useFakeTimers({ doNotFake: ["queueMicrotask"] }); // anything that we don't have a specific matcher for silently returns a 404 fetchMock.catch(404); @@ -153,10 +148,10 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe } // Allow in-flight things to complete before we tear down the test - await jest.runAllTimersAsync(); + await vi.runAllTimersAsync(); fetchMock.mockReset(); - jest.restoreAllMocks(); + vi.restoreAllMocks(); }); async function initTestClient(opts: Partial = {}): Promise { @@ -260,7 +255,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe }); it("handles error on backup query gracefully", async () => { - jest.spyOn(console, "error").mockImplementation(() => {}); + vi.spyOn(console, "error").mockImplementation(() => {}); fetchMock.get( "express:/_matrix/client/v3/room_keys/keys/:room_id/:session_id", @@ -412,13 +407,13 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe } it("Should import full backup in chunks", async function () { - const importMockImpl = jest.fn(); + const importMockImpl = vi.fn(); if (isNewBackend) { // @ts-ignore - mock a private method for testing purpose - jest.spyOn(aliceCrypto.backupManager, "importBackedUpRoomKeys").mockImplementation(importMockImpl); + vi.spyOn(aliceCrypto.backupManager, "importBackedUpRoomKeys").mockImplementation(importMockImpl); } else { // @ts-ignore - mock a private method for testing purpose - jest.spyOn(aliceCrypto, "importBackedUpRoomKeys").mockImplementation(importMockImpl); + vi.spyOn(aliceCrypto, "importBackedUpRoomKeys").mockImplementation(importMockImpl); } // We need several rooms with several sessions to test chunking @@ -433,7 +428,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe check!.backupInfo!.version!, ); - const progressCallback = jest.fn(); + const progressCallback = vi.fn(); const result = await (isNewBackend ? aliceCrypto.restoreKeyBackup({ progressCallback, @@ -480,7 +475,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe }); it("Should continue to process backup if a chunk import fails and report failures", async function () { - const importMockImpl = jest + const importMockImpl = vi .fn() .mockImplementationOnce(() => { // Fail to import first chunk @@ -491,10 +486,10 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe if (isNewBackend) { // @ts-ignore - mock a private method for testing purpose - jest.spyOn(aliceCrypto.backupManager, "importBackedUpRoomKeys").mockImplementation(importMockImpl); + vi.spyOn(aliceCrypto.backupManager, "importBackedUpRoomKeys").mockImplementation(importMockImpl); } else { // @ts-ignore - mock a private method for testing purpose - jest.spyOn(aliceCrypto, "importBackedUpRoomKeys").mockImplementation(importMockImpl); + vi.spyOn(aliceCrypto, "importBackedUpRoomKeys").mockImplementation(importMockImpl); } const { response, expectedTotal } = createBackupDownloadResponse([100, 300]); @@ -507,7 +502,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe check!.backupInfo!.version!, ); - const progressCallback = jest.fn(); + const progressCallback = vi.fn(); const result = await (isNewBackend ? aliceCrypto.restoreKeyBackup({ progressCallback }) : aliceClient.restoreKeyBackupWithRecoveryKey( @@ -541,13 +536,13 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe it("Should continue if some keys fails to decrypt", async function () { // @ts-ignore - mock a private method for testing purpose - aliceCrypto.importBackedUpRoomKeys = jest.fn(); + aliceCrypto.importBackedUpRoomKeys = vi.fn(); const decryptionFailureCount = 2; const mockDecryptor = { // DecryptSessions does not reject on decryption failure, but just skip the key - decryptSessions: jest.fn().mockImplementation((sessions) => { + decryptSessions: vi.fn().mockImplementation((sessions) => { // simulate fail to decrypt 2 keys out of all const decrypted = []; const keys = Object.keys(sessions); @@ -558,11 +553,11 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe } return decrypted; }), - free: jest.fn(), + free: vi.fn(), }; // @ts-ignore - mock a private method for testing purpose - aliceCrypto.getBackupDecryptor = jest.fn().mockResolvedValue(mockDecryptor); + aliceCrypto.getBackupDecryptor = vi.fn().mockResolvedValue(mockDecryptor); const { response, expectedTotal } = createBackupDownloadResponse([100]); @@ -612,7 +607,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe "Should get the decryption key from the secret storage and restore the key backup", async function () { // @ts-ignore - mock a private method for testing purpose - jest.spyOn(aliceCrypto.secretStorage, "get").mockResolvedValue(testData.BACKUP_DECRYPTION_KEY_BASE64); + vi.spyOn(aliceCrypto.secretStorage, "get").mockResolvedValue(testData.BACKUP_DECRYPTION_KEY_BASE64); const fullBackup = { rooms: { @@ -704,9 +699,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe }); }); - fetchMock.get("path:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA, { - overwriteRoutes: true, - }); + fetchMock.get("path:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA); const result = await aliceCrypto.checkKeyBackupAndEnable(); expect(result).toBeTruthy(); @@ -714,7 +707,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe await aliceCrypto.importRoomKeys(someRoomKeys); // The backup loop is waiting a random amount of time to avoid different clients firing at the same time. - jest.runAllTimers(); + vi.runAllTimers(); await Promise.all(uploadPromises); @@ -738,7 +731,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe await aliceCrypto.importRoomKeys([newKey]); - jest.runAllTimers(); + vi.runAllTimers(); await newKeyUploadPromise; }); @@ -762,9 +755,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe const someRoomKeys = testData.MEGOLM_SESSION_DATA_ARRAY; - fetchMock.get("path:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA, { - overwriteRoutes: true, - }); + fetchMock.get("path:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA); const result = await aliceCrypto.checkKeyBackupAndEnable(); expect(result).toBeTruthy(); @@ -773,7 +764,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe await aliceCrypto.importRoomKeys(someRoomKeys); // The backup loop is waiting a random amount of time to avoid different clients firing at the same time. - jest.runAllTimers(); + vi.runAllTimers(); // wait for all keys to be backed up await remainingZeroPromise; @@ -785,9 +776,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe // Let's simulate that a new backup is available by returning error code on key upload - fetchMock.get("path:/_matrix/client/v3/room_keys/version", newBackup, { - overwriteRoutes: true, - }); + fetchMock.get("path:/_matrix/client/v3/room_keys/version", newBackup); // If we import a new key the loop will try to upload to old version, it will // fail then check the current version and switch if trusted @@ -830,12 +819,12 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe await aliceCrypto.importRoomKeys([newKey]); - jest.runAllTimers(); + vi.runAllTimers(); await disableOldBackup; await enableNewBackup; - jest.runAllTimers(); + vi.runAllTimers(); await Promise.all(uploadPromises); await newKeyUploadPromise; @@ -850,22 +839,14 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe await waitForDeviceList(); await aliceCrypto.setDeviceVerified(testData.TEST_USER_ID, testData.TEST_DEVICE_ID); - fetchMock.get("path:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA, { - overwriteRoutes: true, - }); + fetchMock.get("path:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA); // on the first key upload attempt, simulate a network failure const failurePromise = new Promise((resolve) => { - fetchMock.put( - "path:/_matrix/client/v3/room_keys/keys", - () => { - resolve(undefined); - throw new TypeError(`Failed to fetch`); - }, - { - overwriteRoutes: true, - }, - ); + fetchMock.put("path:/_matrix/client/v3/room_keys/keys", () => { + resolve(undefined); + throw new TypeError(`Failed to fetch`); + }); }); // kick the import loop off and wait for the failed request @@ -874,27 +855,21 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe const result = await aliceCrypto.checkKeyBackupAndEnable(); expect(result).toBeTruthy(); - jest.advanceTimersByTime(10 * 60 * 1000); + vi.advanceTimersByTime(10 * 60 * 1000); await failurePromise; // Fix the endpoint to do successful uploads const successPromise = new Promise((resolve) => { - fetchMock.put( - "path:/_matrix/client/v3/room_keys/keys", - () => { - resolve(undefined); - return { - status: 200, - body: { - count: 2, - etag: "abcdefg", - }, - }; - }, - { - overwriteRoutes: true, - }, - ); + fetchMock.put("path:/_matrix/client/v3/room_keys/keys", () => { + resolve(undefined); + return { + status: 200, + body: { + count: 2, + etag: "abcdefg", + }, + }; + }); }); // check that a `KeyBackupSessionsRemaining` event is emitted with `remaining == 0` @@ -907,7 +882,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe }); // run the timers, which will make the backup loop redo the request - await jest.advanceTimersByTimeAsync(10 * 60 * 1000); + await vi.advanceTimersByTimeAsync(10 * 60 * 1000); await successPromise; await allKeysUploadedPromise; }); @@ -934,9 +909,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe // Serve a backup with no trusted signature const unsignedBackup = JSON.parse(JSON.stringify(testData.SIGNED_BACKUP_DATA)); delete unsignedBackup.auth_data.signatures; - fetchMock.get("express:/_matrix/client/v3/room_keys/version", unsignedBackup, { - overwriteRoutes: true, - }); + fetchMock.get("express:/_matrix/client/v3/room_keys/version", unsignedBackup); const checked = await aliceCrypto.checkKeyBackupAndEnable(); expect(checked?.backupInfo?.version).toStrictEqual(unsignedBackup.version); @@ -946,9 +919,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe expect(backupStatus).toBeNull(); // Add a valid signature to the backup - fetchMock.get("express:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA, { - overwriteRoutes: true, - }); + fetchMock.get("express:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA); // check that signalling is working const backupPromise = new Promise((resolve, reject) => { @@ -986,9 +957,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe expect(await aliceCrypto.getKeyBackupInfo()).toBeNull(); // Return now the backup - fetchMock.get("express:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA, { - overwriteRoutes: true, - }); + fetchMock.get("express:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA); expect(await aliceCrypto.getKeyBackupInfo()).toStrictEqual(testData.SIGNED_BACKUP_DATA); @@ -1112,9 +1081,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe delete unsignedBackup.auth_data.signatures; unsignedBackup.version = "2"; - fetchMock.get("path:/_matrix/client/v3/room_keys/version", unsignedBackup, { - overwriteRoutes: true, - }); + fetchMock.get("path:/_matrix/client/v3/room_keys/version", unsignedBackup); await aliceCrypto.checkKeyBackupAndEnable(); expect(await aliceCrypto.getActiveSessionBackupVersion()).toBeNull(); @@ -1139,9 +1106,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe const newBackup = JSON.parse(JSON.stringify(testData.SIGNED_BACKUP_DATA)); newBackup.version = newBackupVersion; - fetchMock.get("path:/_matrix/client/v3/room_keys/version", newBackup, { - overwriteRoutes: true, - }); + fetchMock.get("path:/_matrix/client/v3/room_keys/version", newBackup); await aliceCrypto.checkKeyBackupAndEnable(); expect(await aliceCrypto.getActiveSessionBackupVersion()).toEqual(newBackupVersion); @@ -1162,19 +1127,13 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe expect(result).toBeTruthy(); expect(await aliceCrypto.getActiveSessionBackupVersion()).toEqual(testData.SIGNED_BACKUP_DATA.version); - fetchMock.get( - "path:/_matrix/client/v3/room_keys/version", - { - status: 404, - body: { - errcode: "M_NOT_FOUND", - error: "No backup found", - }, - }, - { - overwriteRoutes: true, + fetchMock.get("path:/_matrix/client/v3/room_keys/version", { + status: 404, + body: { + errcode: "M_NOT_FOUND", + error: "No backup found", }, - ); + }); const noResult = await aliceCrypto.checkKeyBackupAndEnable(); expect(noResult).toBeNull(); expect(await aliceCrypto.getActiveSessionBackupVersion()).toBeNull(); @@ -1217,26 +1176,22 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe // First ensure that the client checks for keys using the backup version 1 /// ===== - fetchMock.get( - "express:/_matrix/client/v3/room_keys/keys/:room_id/:session_id", - (url, request) => { - // check that the version is correct - const version = new URLSearchParams(new URL(url).search).get("version"); - if (version == "1") { - return testData.CURVE25519_KEY_BACKUP_DATA; - } else { - return { - status: 403, - body: { - current_version: "1", - errcode: "M_WRONG_ROOM_KEYS_VERSION", - error: "Wrong backup version.", - }, - }; - } - }, - { overwriteRoutes: true }, - ); + fetchMock.get("express:/_matrix/client/v3/room_keys/keys/:room_id/:session_id", (url, request) => { + // check that the version is correct + const version = new URLSearchParams(new URL(url).search).get("version"); + if (version == "1") { + return testData.CURVE25519_KEY_BACKUP_DATA; + } else { + return { + status: 403, + body: { + current_version: "1", + errcode: "M_WRONG_ROOM_KEYS_VERSION", + error: "Wrong backup version.", + }, + }; + } + }); // Send Alice a message that she won't be able to decrypt, and check that she fetches the key from the backup. syncResponder.sendOrQueueSyncResponse(SYNC_RESPONSE); @@ -1257,7 +1212,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe version: "2", }; - fetchMock.get("path:/_matrix/client/v3/room_keys/version", newBackup, { overwriteRoutes: true }); + fetchMock.get("path:/_matrix/client/v3/room_keys/version", newBackup); // suppose the new key is now known const aliceCrypto = aliceClient.getCrypto()!; await aliceCrypto.storeSessionBackupPrivateKey( @@ -1270,28 +1225,24 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe const awaitHasQueriedNewBackup: IDeferred = defer(); - fetchMock.get( - "express:/_matrix/client/v3/room_keys/keys/:room_id/:session_id", - (url, request) => { - // check that the version is correct - const version = new URLSearchParams(new URL(url).search).get("version"); - if (version == newBackup.version) { - awaitHasQueriedNewBackup.resolve(); - return testData.CURVE25519_KEY_BACKUP_DATA; - } else { - // awaitHasQueriedOldBackup.resolve(); - return { - status: 403, - body: { - current_version: "2", - errcode: "M_WRONG_ROOM_KEYS_VERSION", - error: "Wrong backup version.", - }, - }; - } - }, - { overwriteRoutes: true }, - ); + fetchMock.get("express:/_matrix/client/v3/room_keys/keys/:room_id/:session_id", (url, request) => { + // check that the version is correct + const version = new URLSearchParams(new URL(url).search).get("version"); + if (version == newBackup.version) { + awaitHasQueriedNewBackup.resolve(); + return testData.CURVE25519_KEY_BACKUP_DATA; + } else { + // awaitHasQueriedOldBackup.resolve(); + return { + status: 403, + body: { + current_version: "2", + errcode: "M_WRONG_ROOM_KEYS_VERSION", + error: "Wrong backup version.", + }, + }; + } + }); // Send Alice a message that she won't be able to decrypt, and check that she fetches the key from the new backup. const newMessage: Partial = { @@ -1327,7 +1278,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe // user will be one). syncResponder.sendOrQueueSyncResponse({}); // DeviceList has a sleep(5) which we need to make happen - await jest.advanceTimersByTimeAsync(10); + await vi.advanceTimersByTimeAsync(10); // The client should now know about the dummy device const devices = await aliceClient.getCrypto()!.getUserDeviceInfo([TEST_USER_ID]); diff --git a/spec/integ/crypto/olm-encryption-spec.ts b/spec/integ/crypto/olm-encryption-spec.ts index 5b98c63936a..67c12cd907f 100644 --- a/spec/integ/crypto/olm-encryption-spec.ts +++ b/spec/integ/crypto/olm-encryption-spec.ts @@ -26,6 +26,8 @@ limitations under the License. */ // load olm before the sdk if possible +import { afterEach, beforeEach, describe, expect, it } from "vitest"; + import "../../olm-loader"; import type { Session } from "@matrix-org/olm"; diff --git a/spec/integ/crypto/olm-utils.ts b/spec/integ/crypto/olm-utils.ts index a257ea7c985..994ad22098b 100644 --- a/spec/integ/crypto/olm-utils.ts +++ b/spec/integ/crypto/olm-utils.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { expect } from "vitest"; + import Olm from "@matrix-org/olm"; import anotherjson from "another-json"; diff --git a/spec/integ/crypto/rust-crypto.spec.ts b/spec/integ/crypto/rust-crypto.spec.ts index 5aee7e83582..dec0f406750 100644 --- a/spec/integ/crypto/rust-crypto.spec.ts +++ b/spec/integ/crypto/rust-crypto.spec.ts @@ -14,9 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + import "fake-indexeddb/auto"; import { IDBFactory } from "fake-indexeddb"; -import fetchMock from "fetch-mock-jest"; +import fetchMock from "@fetch-mock/vitest"; import { createClient, CryptoEvent, IndexedDBCryptoStore } from "../../../src"; import { populateStore } from "../../test-utils/test_indexeddb_cryptostore_dump"; @@ -25,7 +27,7 @@ import { IDENTITY_NOT_TRUSTED_DATASET } from "../../test-utils/test_indexeddb_cr import { FULL_ACCOUNT_DATASET } from "../../test-utils/test_indexeddb_cryptostore_dump/full_account"; import { EMPTY_ACCOUNT_DATASET } from "../../test-utils/test_indexeddb_cryptostore_dump/empty_account"; -jest.setTimeout(15000); +vi.setTimeout(15000); afterEach(() => { // reset fake-indexeddb after each test, to make sure we don't leak connections @@ -116,10 +118,6 @@ describe("MatrixClient.initRustCrypto", () => { }); describe("Libolm Migration", () => { - beforeEach(() => { - fetchMock.reset(); - }); - it("should migrate from libolm", async () => { fetchMock.get("path:/_matrix/client/v3/room_keys/version", FULL_ACCOUNT_DATASET.backupResponse); @@ -137,7 +135,7 @@ describe("MatrixClient.initRustCrypto", () => { pickleKey: FULL_ACCOUNT_DATASET.pickleKey, }); - const progressListener = jest.fn(); + const progressListener = vi.fn(); matrixClient.addListener(CryptoEvent.LegacyCryptoStoreMigrationProgress, progressListener); await matrixClient.initRustCrypto(); @@ -308,7 +306,7 @@ describe("MatrixClient.initRustCrypto", () => { }); // When we start Rust crypto, potentially triggering an upgrade - const progressListener = jest.fn(); + const progressListener = vi.fn(); matrixClient.addListener(CryptoEvent.LegacyCryptoStoreMigrationProgress, progressListener); await matrixClient.initRustCrypto(); diff --git a/spec/integ/crypto/to-device-messages.spec.ts b/spec/integ/crypto/to-device-messages.spec.ts index ee15266ce98..5cb0d4a9543 100644 --- a/spec/integ/crypto/to-device-messages.spec.ts +++ b/spec/integ/crypto/to-device-messages.spec.ts @@ -14,7 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -import fetchMock from "fetch-mock-jest"; +import { afterEach, beforeEach, describe, expect, it } from "vitest"; + +import fetchMock from "@fetch-mock/vitest"; import "fake-indexeddb/auto"; import { IDBFactory } from "fake-indexeddb"; diff --git a/spec/integ/crypto/verification.spec.ts b/spec/integ/crypto/verification.spec.ts index a4cee9e8365..bffce52e468 100644 --- a/spec/integ/crypto/verification.spec.ts +++ b/spec/integ/crypto/verification.spec.ts @@ -14,11 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { afterEach, beforeAll, beforeEach, describe, expect, it, test, vi } from "vitest"; + import "fake-indexeddb/auto"; import anotherjson from "another-json"; import FetchMock from "fetch-mock"; -import fetchMock from "fetch-mock-jest"; +import fetchMock from "@fetch-mock/vitest"; import { IDBFactory } from "fake-indexeddb"; import { createHash } from "crypto"; import Olm from "@matrix-org/olm"; @@ -87,7 +89,7 @@ import { encodeBase64 } from "../../../src/base64"; // The verification flows use javascript timers to set timeouts. We tell jest to use mock timer implementations // to ensure that we don't end up with dangling timeouts. // But the wasm bindings of matrix-sdk-crypto rely on a working `queueMicrotask`. -jest.useFakeTimers({ doNotFake: ["queueMicrotask"] }); +vi.useFakeTimers({ doNotFake: ["queueMicrotask"] }); beforeAll(async () => { // we use the libolm primitives in the test, so init the Olm library @@ -154,7 +156,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("verification (%s)", (backend: st } // Allow in-flight things to complete before we tear down the test - await jest.runAllTimersAsync(); + await vi.runAllTimersAsync(); fetchMock.mockReset(); }); @@ -253,7 +255,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("verification (%s)", (backend: st const sendToDevicePromise = expectSendToDeviceMessage("m.key.verification.accept"); const verificationPromise = verifier.verify(); // advance the clock, because the devicelist likes to sleep for 5ms during key downloads - jest.advanceTimersByTime(10); + vi.advanceTimersByTime(10); requestBody = await sendToDevicePromise; toDeviceMessage = requestBody.messages[TEST_USER_ID][TEST_DEVICE_ID]; @@ -331,7 +333,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("verification (%s)", (backend: st expect(request.otherPartySupportsMethod("m.sas.v1")).toBe(true); // advance the clock, because the devicelist likes to sleep for 5ms during key downloads - await jest.advanceTimersByTimeAsync(10); + await vi.advanceTimersByTimeAsync(10); // And now Alice starts a SAS verification let sendToDevicePromise = expectSendToDeviceMessage("m.key.verification.start"); @@ -654,7 +656,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("verification (%s)", (backend: st expect(request.verifier).toBeUndefined(); // advance the clock, because the devicelist likes to sleep for 5ms during key downloads - await jest.advanceTimersByTimeAsync(10); + await vi.advanceTimersByTimeAsync(10); // ... but Alice wants to do an SAS verification const sendToDevicePromise = expectSendToDeviceMessage("m.key.verification.start"); @@ -699,7 +701,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("verification (%s)", (backend: st expect(request.verifier).toBeUndefined(); // advance the clock, because the devicelist likes to sleep for 5ms during key downloads - await jest.advanceTimersByTimeAsync(10); + await vi.advanceTimersByTimeAsync(10); // ... but the dummy device wants to do an SAS verification returnToDeviceMessageFromSync(buildSasStartMessage(transactionId)); @@ -778,7 +780,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("verification (%s)", (backend: st const sendToDevicePromise = expectSendToDeviceMessage("m.key.verification.accept"); const verificationPromise = verifier.verify(); // advance the clock, because the devicelist likes to sleep for 5ms during key downloads - jest.advanceTimersByTime(10); + vi.advanceTimersByTime(10); await sendToDevicePromise; // now we unceremoniously cancel. We expect the verificatationPromise to reject. @@ -959,7 +961,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("verification (%s)", (backend: st // In `DeviceList#doQueuedQueries`, the key download response is processed every 5ms // 5ms by users, ie Bob and Alice - await jest.advanceTimersByTimeAsync(10); + await vi.advanceTimersByTimeAsync(10); const messageRequestPromise = awaitRoomMessageRequest(); const verificationRequest = await aliceClient @@ -1072,7 +1074,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("verification (%s)", (backend: st }); it("ignores old verification requests", async () => { - const eventHandler = jest.fn(); + const eventHandler = vi.fn(); aliceClient.on(CryptoEvent.VerificationRequestReceived, eventHandler); const verificationRequestEvent = createVerificationRequestEvent(); @@ -1137,7 +1139,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("verification (%s)", (backend: st returnToDeviceMessageFromSync(toDeviceEvent); // advance the clock, because the devicelist likes to sleep for 5ms during key downloads - await jest.advanceTimersByTimeAsync(10); + await vi.advanceTimersByTimeAsync(10); // Wait for the request to be decrypted const request1 = await requestEventPromise; @@ -1176,7 +1178,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("verification (%s)", (backend: st expect(matrixEvent.getContent().msgtype).toEqual("m.bad.encrypted"); // Advance time by 5mins, the verification request should be ignored after that - jest.advanceTimersByTime(5 * 60 * 1000); + vi.advanceTimersByTime(5 * 60 * 1000); // Send Bob the room keys returnToDeviceMessageFromSync(toDeviceEvent); @@ -1243,7 +1245,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("verification (%s)", (backend: st syncResponder.sendOrQueueSyncResponse(getSyncResponse([TEST_USER_ID])); await syncPromise(aliceClient); // DeviceList has a sleep(5) which we need to make happen - await jest.advanceTimersByTimeAsync(10); + await vi.advanceTimersByTimeAsync(10); // The client should now know about the olm device const devices = await aliceClient.getCrypto()!.getUserDeviceInfo([TEST_USER_ID]); @@ -1255,7 +1257,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("verification (%s)", (backend: st testOlmAccount?.free(); // Allow in-flight things to complete before we tear down the test - await jest.runAllTimersAsync(); + await vi.runAllTimersAsync(); fetchMock.mockReset(); }); @@ -1300,11 +1302,11 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("verification (%s)", (backend: st await sendBackupGossipAndExpectVersion(requestId!, BACKUP_DECRYPTION_KEY_BASE64, nonMatchingBackupInfo); // We are lacking a way to signal that the secret has been received, so we wait a bit.. - jest.useRealTimers(); + vi.useRealTimers(); await new Promise((resolve) => { setTimeout(resolve, 500); }); - jest.useFakeTimers({ doNotFake: ["queueMicrotask"] }); + vi.useFakeTimers({ doNotFake: ["queueMicrotask"] }); // the backup secret should not be cached const cachedKey = await aliceClient.getCrypto()!.getSessionBackupPrivateKey(); @@ -1324,11 +1326,11 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("verification (%s)", (backend: st await sendBackupGossipAndExpectVersion(requestId!, BACKUP_DECRYPTION_KEY_BASE64, infoCopy); // We are lacking a way to signal that the secret has been received, so we wait a bit.. - jest.useRealTimers(); + vi.useRealTimers(); await new Promise((resolve) => { setTimeout(resolve, 500); }); - jest.useFakeTimers({ doNotFake: ["queueMicrotask"] }); + vi.useFakeTimers({ doNotFake: ["queueMicrotask"] }); // the backup secret should not be cached const cachedKey = await aliceClient.getCrypto()!.getSessionBackupPrivateKey(); @@ -1349,11 +1351,11 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("verification (%s)", (backend: st ); // We are lacking a way to signal that the secret has been received, so we wait a bit.. - jest.useRealTimers(); + vi.useRealTimers(); await new Promise((resolve) => { setTimeout(resolve, 500); }); - jest.useFakeTimers({ doNotFake: ["queueMicrotask"] }); + vi.useFakeTimers({ doNotFake: ["queueMicrotask"] }); // the backup secret should not be cached const cachedKey = await aliceClient.getCrypto()!.getSessionBackupPrivateKey(); @@ -1370,11 +1372,11 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("verification (%s)", (backend: st await sendBackupGossipAndExpectVersion(requestId!, "InvalidSecret", matchingBackupInfo); // We are lacking a way to signal that the secret has been received, so we wait a bit.. - jest.useRealTimers(); + vi.useRealTimers(); await new Promise((resolve) => { setTimeout(resolve, 500); }); - jest.useFakeTimers({ doNotFake: ["queueMicrotask"] }); + vi.useFakeTimers({ doNotFake: ["queueMicrotask"] }); // the backup secret should not be cached const cachedKey = await aliceClient.getCrypto()!.getSessionBackupPrivateKey(); @@ -1493,7 +1495,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("verification (%s)", (backend: st // user will be one). syncResponder.sendOrQueueSyncResponse({}); // DeviceList has a sleep(5) which we need to make happen - await jest.advanceTimersByTimeAsync(10); + await vi.advanceTimersByTimeAsync(10); // The client should now know about the dummy device const devices = await aliceClient.getCrypto()!.getUserDeviceInfo([TEST_USER_ID]); diff --git a/spec/integ/devicelist-integ.spec.ts b/spec/integ/devicelist-integ.spec.ts index ce741d8dc39..82fea0ef30c 100644 --- a/spec/integ/devicelist-integ.spec.ts +++ b/spec/integ/devicelist-integ.spec.ts @@ -16,6 +16,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { afterEach, beforeEach, describe, expect, it } from "vitest"; + import { TestClient } from "../TestClient"; import * as testUtils from "../test-utils/test-utils"; import { logger } from "../../src/logger"; diff --git a/spec/integ/matrix-client-event-emitter.spec.ts b/spec/integ/matrix-client-event-emitter.spec.ts index 07a7233464e..66d69b3ce7e 100644 --- a/spec/integ/matrix-client-event-emitter.spec.ts +++ b/spec/integ/matrix-client-event-emitter.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { afterEach, beforeEach, describe, expect, it } from "vitest"; + import HttpBackend from "matrix-mock-request"; import { diff --git a/spec/integ/matrix-client-event-timeline.spec.ts b/spec/integ/matrix-client-event-timeline.spec.ts index f7e3662cde4..dfc86714228 100644 --- a/spec/integ/matrix-client-event-timeline.spec.ts +++ b/spec/integ/matrix-client-event-timeline.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { afterEach, beforeEach, describe, expect, it } from "vitest"; + import * as utils from "../test-utils/test-utils"; import { ClientEvent, diff --git a/spec/integ/matrix-client-methods.spec.ts b/spec/integ/matrix-client-methods.spec.ts index e058426cbd7..af4a1937e79 100644 --- a/spec/integ/matrix-client-methods.spec.ts +++ b/spec/integ/matrix-client-methods.spec.ts @@ -13,8 +13,9 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ +import { afterEach, beforeEach, describe, expect, it, Mocked, vi } from "vitest"; + import HttpBackend from "matrix-mock-request"; -import { Mocked } from "jest-mock"; import * as utils from "../test-utils/test-utils"; import { CRYPTO_ENABLED, IStoredClientOpts, MatrixClient } from "../../src/client"; @@ -305,23 +306,16 @@ describe("MatrixClient", function () { [ 403, { errcode: "M_FORBIDDEN", error: "You don't have permission to knock" }, - "[M_FORBIDDEN: MatrixError: [403] You don't have permission to knock]", - ], - [ - 500, - { errcode: "INTERNAL_SERVER_ERROR" }, - "[INTERNAL_SERVER_ERROR: MatrixError: [500] Unknown message]", + "MatrixError: [403] You don't have permission to knock", ], + [500, { errcode: "INTERNAL_SERVER_ERROR" }, "MatrixError: [500] Unknown message"], ]; it.each(testCases)("should handle %s error", async (code, { errcode, error }, snapshot) => { httpBackend.when("POST", "/knock/" + encodeURIComponent(roomId)).respond(code, { errcode, error }); const prom = client.knockRoom(roomId); - await Promise.all([ - httpBackend.flushAllExpected(), - expect(prom).rejects.toMatchInlineSnapshot(snapshot), - ]); + await Promise.all([httpBackend.flushAllExpected(), expect(prom).rejects.toThrow(snapshot)]); }); }); }); @@ -1243,7 +1237,7 @@ describe("MatrixClient", function () { describe("logout", () => { it("should abort pending requests when called with stopClient=true", async () => { httpBackend.when("POST", "/logout").respond(200, {}); - const fn = jest.fn(); + const fn = vi.fn(); client.http.request(Method.Get, "/test").catch(fn); client.logout(true); await httpBackend.flush(undefined); @@ -1371,7 +1365,7 @@ describe("MatrixClient", function () { }); afterEach(() => { - jest.useRealTimers(); + vi.useRealTimers(); }); it("should always fetch capabilities and then cache", async () => { @@ -1642,7 +1636,7 @@ describe("MatrixClient", function () { it("should proxy to the crypto backend", async () => { const mockBackend = { - getTrustCrossSignedDevices: jest.fn().mockReturnValue(true), + getTrustCrossSignedDevices: vi.fn().mockReturnValue(true), } as unknown as Mocked; client["cryptoBackend"] = mockBackend; @@ -1659,7 +1653,7 @@ describe("MatrixClient", function () { it("should proxy to the crypto backend", async () => { const mockBackend = { - setTrustCrossSignedDevices: jest.fn(), + setTrustCrossSignedDevices: vi.fn(), } as unknown as Mocked; client["cryptoBackend"] = mockBackend; @@ -1673,7 +1667,7 @@ describe("MatrixClient", function () { describe("setSyncPresence", () => { it("should pass calls through to the underlying sync api", () => { - const setPresence = jest.fn(); + const setPresence = vi.fn(); // @ts-ignore client.syncApi = { setPresence }; client.setSyncPresence(SetPresence.Unavailable); diff --git a/spec/integ/matrix-client-opts.spec.ts b/spec/integ/matrix-client-opts.spec.ts index cb5b1b9068e..52dc8e8e2b3 100644 --- a/spec/integ/matrix-client-opts.spec.ts +++ b/spec/integ/matrix-client-opts.spec.ts @@ -1,3 +1,4 @@ +import { afterEach, beforeEach, describe, expect, it } from "vitest"; import HttpBackend from "matrix-mock-request"; import * as utils from "../test-utils/test-utils"; diff --git a/spec/integ/matrix-client-relations.spec.ts b/spec/integ/matrix-client-relations.spec.ts index 1a7f370c22a..fb957cb9fe3 100644 --- a/spec/integ/matrix-client-relations.spec.ts +++ b/spec/integ/matrix-client-relations.spec.ts @@ -15,6 +15,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { afterEach, beforeEach, describe, expect, it } from "vitest"; + import HttpBackend from "matrix-mock-request"; import { Direction, MatrixClient, MatrixScheduler } from "../../src/matrix"; diff --git a/spec/integ/matrix-client-retrying.spec.ts b/spec/integ/matrix-client-retrying.spec.ts index b78e3c7b445..8e5de363bb0 100644 --- a/spec/integ/matrix-client-retrying.spec.ts +++ b/spec/integ/matrix-client-retrying.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { afterEach, beforeEach, describe, expect, it } from "vitest"; + import HttpBackend from "matrix-mock-request"; import { EventStatus, MatrixClient, MatrixScheduler, MsgType, RoomEvent } from "../../src/matrix"; diff --git a/spec/integ/matrix-client-room-timeline.spec.ts b/spec/integ/matrix-client-room-timeline.spec.ts index 1d0ed86e0d3..efbed029868 100644 --- a/spec/integ/matrix-client-room-timeline.spec.ts +++ b/spec/integ/matrix-client-room-timeline.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { afterEach, beforeEach, describe, expect, it } from "vitest"; + import HttpBackend from "matrix-mock-request"; import * as utils from "../test-utils/test-utils"; diff --git a/spec/integ/matrix-client-syncing-errors.spec.ts b/spec/integ/matrix-client-syncing-errors.spec.ts index 4133ffb9aaa..798800ac130 100644 --- a/spec/integ/matrix-client-syncing-errors.spec.ts +++ b/spec/integ/matrix-client-syncing-errors.spec.ts @@ -14,8 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { beforeEach, describe, expect, it, vi } from "vitest"; + import "fake-indexeddb/auto"; -import fetchMock from "fetch-mock-jest"; +import fetchMock from "@fetch-mock/vitest"; import { MatrixClient, ClientEvent, createClient, SyncState } from "../../src"; @@ -83,7 +85,7 @@ describe("MatrixClient syncing errors", () => { }); it("should retry, until errors are solved.", async () => { - jest.useFakeTimers(); + vi.useFakeTimers(); fetchMock.config.overwriteRoutes = false; fetchMock .getOnce("end:versions", {}) // first version check without credentials needs to succeed @@ -105,18 +107,18 @@ describe("MatrixClient syncing errors", () => { await client!.startClient(); expect(await syncEvents[0].promise).toBe(SyncState.Error); - jest.advanceTimersByTime(60 * 1000); // this will skip forward to trigger the keepAlive/sync + vi.advanceTimersByTime(60 * 1000); // this will skip forward to trigger the keepAlive/sync expect(await syncEvents[1].promise).toBe(SyncState.Error); - jest.advanceTimersByTime(60 * 1000); // this will skip forward to trigger the keepAlive/sync + vi.advanceTimersByTime(60 * 1000); // this will skip forward to trigger the keepAlive/sync expect(await syncEvents[2].promise).toBe(SyncState.Prepared); - jest.advanceTimersByTime(60 * 1000); // this will skip forward to trigger the keepAlive/sync + vi.advanceTimersByTime(60 * 1000); // this will skip forward to trigger the keepAlive/sync expect(await syncEvents[3].promise).toBe(SyncState.Syncing); - jest.advanceTimersByTime(60 * 1000); // this will skip forward to trigger the keepAlive/sync + vi.advanceTimersByTime(60 * 1000); // this will skip forward to trigger the keepAlive/sync expect(await syncEvents[4].promise).toBe(SyncState.Syncing); }); it("should stop sync keep alive when client is stopped.", async () => { - jest.useFakeTimers(); + vi.useFakeTimers(); fetchMock.config.overwriteRoutes = false; fetchMock .get("end:capabilities", {}) @@ -146,9 +148,9 @@ describe("MatrixClient syncing errors", () => { const syntState = await firstSyncEvent.promise; expect(syntState).toBe(SyncState.Error); - jest.runAllTimers(); // this will skip forward to trigger the keepAlive + vi.runAllTimers(); // this will skip forward to trigger the keepAlive - jest.useRealTimers(); // we need real timer for the setTimout below to work + vi.useRealTimers(); // we need real timer for the setTimout below to work const timeoutPromise = makeQueryablePromise(new Promise((res) => setTimeout(res, 1))); diff --git a/spec/integ/matrix-client-syncing.spec.ts b/spec/integ/matrix-client-syncing.spec.ts index 59d29b82b7f..ce3f7c4d27d 100644 --- a/spec/integ/matrix-client-syncing.spec.ts +++ b/spec/integ/matrix-client-syncing.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + import "fake-indexeddb/auto"; import HttpBackend from "matrix-mock-request"; @@ -503,7 +505,7 @@ describe("MatrixClient syncing", () => { }) .respond(200, syncData); - client!.store.getSavedSyncToken = jest.fn().mockResolvedValue("this-is-a-token"); + client!.store.getSavedSyncToken = vi.fn().mockResolvedValue("this-is-a-token"); client!.startClient({ initialSyncLimit: 1 }); return httpBackend!.flushAllExpected(); @@ -996,7 +998,6 @@ describe("MatrixClient syncing", () => { roomVersion: "org.matrix.msc2716v3", }, ].forEach((testMeta) => { - // eslint-disable-next-line jest/valid-title describe(testMeta.label, () => { const roomCreateEvent = utils.mkEvent({ type: "m.room.create", @@ -1837,7 +1838,7 @@ describe("MatrixClient syncing", () => { await Promise.all([httpBackend!.flushAllExpected(), awaitSyncEvent()]); const room = client!.getRoom(roomOne); - room!.hasEncryptionStateEvent = jest.fn().mockReturnValue(true); + room!.hasEncryptionStateEvent = vi.fn().mockReturnValue(true); expect(room!.getThreadUnreadNotificationCount(THREAD_ID, NotificationCountType.Total)).toBe(5); @@ -2521,7 +2522,7 @@ describe("MatrixClient syncing", () => { const eventB2 = new MatrixEvent({ type: "b", content: { body: "2" } }); client!.store.storeAccountDataEvents([eventA1, eventB1]); - const fn = jest.fn(); + const fn = vi.fn(); client!.on(ClientEvent.AccountData, fn); httpBackend!.when("GET", "/sync").respond(200, { diff --git a/spec/integ/matrix-client-unread-notifications.spec.ts b/spec/integ/matrix-client-unread-notifications.spec.ts index a9124e1b067..e2adcd0b049 100644 --- a/spec/integ/matrix-client-unread-notifications.spec.ts +++ b/spec/integ/matrix-client-unread-notifications.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + import "fake-indexeddb/auto"; import HttpBackend from "matrix-mock-request"; @@ -63,7 +65,7 @@ describe("Notification count fixing", () => { client!.startClient({ threadSupport: true }); const room = new Room(roomId, client!, selfUserId); - jest.spyOn(client!, "getRoom").mockImplementation((id) => (id === roomId ? room : null)); + vi.spyOn(client!, "getRoom").mockImplementation((id) => (id === roomId ? room : null)); const event = new MatrixEvent({ room_id: roomId, @@ -78,7 +80,7 @@ describe("Notification count fixing", () => { }, }); - jest.spyOn(event, "getPushActions").mockReturnValue({ + vi.spyOn(event, "getPushActions").mockReturnValue({ notify: true, tweaks: {}, }); @@ -124,7 +126,7 @@ describe("MatrixClient syncing", () => { ]); const room = new Room(roomId, client!, selfUserId); - jest.spyOn(client!, "getRoom").mockImplementation((id) => (id === roomId ? room : null)); + vi.spyOn(client!, "getRoom").mockImplementation((id) => (id === roomId ? room : null)); const thread = mkThread({ room, client: client!, authorId: selfUserId, participantUserIds: [selfUserId] }); const threadReply = thread.events.at(-1)!; @@ -144,7 +146,7 @@ describe("MatrixClient syncing", () => { const reactionEventId = `$9-${Math.random()}-${Math.random()}`; let lastEvent: MatrixEvent | null = null; - jest.spyOn(client! as any, "sendEventHttpRequest").mockImplementation((event) => { + vi.spyOn(client! as any, "sendEventHttpRequest").mockImplementation((event) => { lastEvent = event as MatrixEvent; return { event_id: reactionEventId }; }); @@ -196,7 +198,7 @@ describe("MatrixClient syncing", () => { }) .respond(200, syncData); - client!.store.getSavedSyncToken = jest.fn().mockResolvedValue("this-is-a-token"); + client!.store.getSavedSyncToken = vi.fn().mockResolvedValue("this-is-a-token"); client!.startClient({ initialSyncLimit: 1 }); await httpBackend!.flushAllExpected(); diff --git a/spec/integ/rendezvous/MSC4108SignInWithQR.spec.ts b/spec/integ/rendezvous/MSC4108SignInWithQR.spec.ts index d3bb0a8c6ff..91d97153d0e 100644 --- a/spec/integ/rendezvous/MSC4108SignInWithQR.spec.ts +++ b/spec/integ/rendezvous/MSC4108SignInWithQR.spec.ts @@ -14,9 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { beforeEach, describe, expect, it, vi } from "vitest"; + import { QrCodeData, QrCodeMode } from "@matrix-org/matrix-sdk-crypto-wasm"; -import { mocked } from "jest-mock"; -import fetchMock from "fetch-mock-jest"; +import fetchMock from "@fetch-mock/vitest"; import { MSC4108FailureReason, @@ -37,11 +38,12 @@ import { MatrixHttpApi, } from "../../../src"; import { makeDelegatedAuthConfig } from "../../test-utils/oidc"; +import { mocked } from "../../test-utils"; function makeMockClient(opts: { userId: string; deviceId: string; msc4108Enabled: boolean }): MatrixClient { const baseUrl = "https://example.com"; const crypto = { - exportSecretsForQrLogin: jest.fn(), + exportSecretsForQrLogin: vi.fn(), }; const client = { doesServerSupportUnstableFeature(feature: string) { @@ -55,9 +57,9 @@ function makeMockClient(opts: { userId: string; deviceId: string; msc4108Enabled }, baseUrl, getDomain: () => "example.com", - getDevice: jest.fn(), - getCrypto: jest.fn(() => crypto), - getAuthMetadata: jest.fn().mockResolvedValue(makeDelegatedAuthConfig("https://issuer/", [DEVICE_CODE_SCOPE])), + getDevice: vi.fn(), + getCrypto: vi.fn(() => crypto), + getAuthMetadata: vi.fn().mockResolvedValue(makeDelegatedAuthConfig("https://issuer/", [DEVICE_CODE_SCOPE])), } as unknown as MatrixClient; client.http = new MatrixHttpApi(client, { baseUrl: client.baseUrl, @@ -78,10 +80,6 @@ describe("MSC4108SignInWithQR", () => { }); }); - afterEach(() => { - fetchMock.reset(); - }); - const url = "https://fallbackserver/rz/123"; const deviceId = "DEADB33F"; const verificationUri = "https://example.com/verify"; @@ -116,10 +114,10 @@ describe("MSC4108SignInWithQR", () => { let opponentData = defer(); const ourMockSession = { - send: jest.fn(async (newData) => { + send: vi.fn(async (newData) => { ourData.resolve(newData); }), - receive: jest.fn(() => { + receive: vi.fn(() => { const prom = opponentData.promise; prom.then(() => { opponentData = defer(); @@ -135,10 +133,10 @@ describe("MSC4108SignInWithQR", () => { }, } as unknown as MSC4108RendezvousSession; const opponentMockSession = { - send: jest.fn(async (newData) => { + send: vi.fn(async (newData) => { opponentData.resolve(newData); }), - receive: jest.fn(() => { + receive: vi.fn(() => { const prom = ourData.promise; prom.then(() => { ourData = defer(); @@ -250,7 +248,7 @@ describe("MSC4108SignInWithQR", () => { const secrets = { cross_signing: { master_key: "mk", user_signing_key: "usk", self_signing_key: "ssk" }, }; - client.getCrypto()!.exportSecretsBundle = jest.fn().mockResolvedValue(secrets); + client.getCrypto()!.exportSecretsBundle = vi.fn().mockResolvedValue(secrets); const payload = { secrets: expect.objectContaining(secrets), @@ -262,12 +260,12 @@ describe("MSC4108SignInWithQR", () => { }); it("should abort if device doesn't come up by timeout", async () => { - jest.spyOn(globalThis, "setTimeout").mockImplementation((fn) => { + vi.spyOn(globalThis, "setTimeout").mockImplementation((fn) => { fn(); // TODO: mock timers properly return -1 as any; }); - jest.spyOn(Date, "now").mockImplementation(() => { + vi.spyOn(Date, "now").mockImplementation(() => { return 12345678 + mocked(setTimeout).mock.calls.length * 1000; }); @@ -315,7 +313,7 @@ describe("MSC4108SignInWithQR", () => { }); it("should not send secrets if user cancels", async () => { - jest.spyOn(globalThis, "setTimeout").mockImplementation((fn) => { + vi.spyOn(globalThis, "setTimeout").mockImplementation((fn) => { fn(); // TODO: mock timers properly return -1 as any; @@ -343,7 +341,7 @@ describe("MSC4108SignInWithQR", () => { const secrets = { cross_signing: { master_key: "mk", user_signing_key: "usk", self_signing_key: "ssk" }, }; - client.getCrypto()!.exportSecretsBundle = jest.fn().mockResolvedValue(secrets); + client.getCrypto()!.exportSecretsBundle = vi.fn().mockResolvedValue(secrets); await Promise.all([ expect(ourProm).rejects.toThrow("User cancelled"), diff --git a/spec/integ/sliding-sync-sdk.spec.ts b/spec/integ/sliding-sync-sdk.spec.ts index 1519212060d..dc867cd4d22 100644 --- a/spec/integ/sliding-sync-sdk.spec.ts +++ b/spec/integ/sliding-sync-sdk.spec.ts @@ -15,6 +15,8 @@ limitations under the License. */ // eslint-disable-next-line no-restricted-imports +import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest"; + import MockHttpBackend from "matrix-mock-request"; import { fail } from "assert"; @@ -63,17 +65,17 @@ describe("SlidingSyncSdk", () => { const selfAccessToken = "aseukfgwef"; const mockifySlidingSync = (s: SlidingSync): SlidingSync => { - s.getListParams = jest.fn(); - s.getListData = jest.fn(); - s.getRoomSubscriptions = jest.fn(); - s.modifyRoomSubscriptionInfo = jest.fn(); - s.modifyRoomSubscriptions = jest.fn(); - s.registerExtension = jest.fn(); - s.setList = jest.fn(); - s.setListRanges = jest.fn(); - s.start = jest.fn(); - s.stop = jest.fn(); - s.resend = jest.fn(); + s.getListParams = vi.fn(); + s.getListData = vi.fn(); + s.getRoomSubscriptions = vi.fn(); + s.modifyRoomSubscriptionInfo = vi.fn(); + s.modifyRoomSubscriptions = vi.fn(); + s.registerExtension = vi.fn(); + s.setList = vi.fn(); + s.setListRanges = vi.fn(); + s.start = vi.fn(); + s.stop = vi.fn(); + s.resend = vi.fn(); return s; }; @@ -145,7 +147,7 @@ describe("SlidingSyncSdk", () => { // find an extension on a SlidingSyncSdk instance const findExtension = (name: string): Extension => { expect(mockSlidingSync!.registerExtension).toHaveBeenCalled(); - const mockFn = mockSlidingSync!.registerExtension as jest.Mock; + const mockFn = mockSlidingSync!.registerExtension; // find the extension for (let i = 0; i < mockFn.mock.calls.length; i++) { const calledExtension = mockFn.mock.calls[i][0] as Extension; @@ -651,7 +653,7 @@ describe("SlidingSyncSdk", () => { }); it("can update device lists", () => { - syncCryptoCallback!.processDeviceLists = jest.fn(); + syncCryptoCallback!.processDeviceLists = vi.fn(); ext.onResponse({ device_lists: { changed: ["@alice:localhost"], @@ -665,7 +667,7 @@ describe("SlidingSyncSdk", () => { }); it("can update OTK counts and unused fallback keys", () => { - syncCryptoCallback!.processKeyCounts = jest.fn(); + syncCryptoCallback!.processKeyCounts = vi.fn(); ext.onResponse({ device_one_time_keys_count: { signed_curve25519: 42, diff --git a/spec/integ/sliding-sync.spec.ts b/spec/integ/sliding-sync.spec.ts index 29cd3443bfe..27d4d02764e 100644 --- a/spec/integ/sliding-sync.spec.ts +++ b/spec/integ/sliding-sync.spec.ts @@ -15,6 +15,8 @@ limitations under the License. */ // eslint-disable-next-line no-restricted-imports +import { afterAll, beforeAll, describe, expect, it } from "vitest"; + import EventEmitter from "events"; import MockHttpBackend from "matrix-mock-request"; @@ -1197,7 +1199,7 @@ describe("SlidingSync", () => { httpBackend!.when("POST", syncUrl).check(pushTxn).respond(200, { pos: "B" }); // missing txn_id await httpBackend!.flushAllExpected(); - // attach rejection handlers now else if we do it later Jest treats that as an unhandled rejection + // attach rejection handlers now else if we do it later Vitest treats that as an unhandled rejection // which is a fail. const C = slidingSync.setListRanges("a", [[0, 20]]); diff --git a/spec/setupTests.ts b/spec/setupTests.ts index 2c16f663262..ab431ceb931 100644 --- a/spec/setupTests.ts +++ b/spec/setupTests.ts @@ -14,8 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. */ -jest.mock("../src/http-api/utils", () => ({ - ...jest.requireActual("../src/http-api/utils"), - // We mock timeoutSignal otherwise it causes tests to leave timers running - timeoutSignal: () => new AbortController().signal, -})); +import { vi } from "vitest"; +import { manageFetchMockGlobally } from "@fetch-mock/vitest"; + +export default function setup() { + manageFetchMockGlobally(); + + vi.mock("../src/http-api/utils", async () => ({ + ...(await vi.importActual("../src/http-api/utils")), + // We mock timeoutSignal otherwise it causes tests to leave timers running + timeoutSignal: () => new AbortController().signal, + })); +} diff --git a/spec/slowReporter.cjs b/spec/slowReporter.cjs deleted file mode 100644 index b50d9ea79de..00000000000 --- a/spec/slowReporter.cjs +++ /dev/null @@ -1,100 +0,0 @@ -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/* eslint-disable no-console */ - -class JestSlowTestReporter { - constructor(globalConfig, options) { - this._globalConfig = globalConfig; - this._options = options; - this._slowTests = []; - this._slowTestSuites = []; - } - - onRunComplete() { - const displayResult = (result, isTestSuite) => { - if (!isTestSuite) console.log(); - - result.sort((a, b) => b.duration - a.duration); - const rootPathRegex = new RegExp(`^${process.cwd()}`); - const slowestTests = result.slice(0, this._options.numTests || 10); - const slowTestTime = this._slowTestTime(slowestTests); - const allTestTime = this._allTestTime(result); - const percentTime = (slowTestTime / allTestTime) * 100; - - if (isTestSuite) { - console.log( - `Top ${slowestTests.length} slowest test suites (${slowTestTime / 1000} seconds,` + - ` ${percentTime.toFixed(1)}% of total time):`, - ); - } else { - console.log( - `Top ${slowestTests.length} slowest tests (${slowTestTime / 1000} seconds,` + - ` ${percentTime.toFixed(1)}% of total time):`, - ); - } - - for (let i = 0; i < slowestTests.length; i++) { - const duration = slowestTests[i].duration; - const filePath = slowestTests[i].filePath.replace(rootPathRegex, "."); - - if (isTestSuite) { - console.log(` ${duration / 1000} seconds ${filePath}`); - } else { - const fullName = slowestTests[i].fullName; - console.log(` ${fullName}`); - console.log(` ${duration / 1000} seconds ${filePath}`); - } - } - console.log(); - }; - - displayResult(this._slowTests); - displayResult(this._slowTestSuites, true); - } - - onTestResult(test, testResult) { - this._slowTestSuites.push({ - duration: testResult.perfStats.runtime, - filePath: testResult.testFilePath, - }); - for (let i = 0; i < testResult.testResults.length; i++) { - this._slowTests.push({ - duration: testResult.testResults[i].duration, - fullName: testResult.testResults[i].fullName, - filePath: testResult.testFilePath, - }); - } - } - - _slowTestTime(slowestTests) { - let slowTestTime = 0; - for (let i = 0; i < slowestTests.length; i++) { - slowTestTime += slowestTests[i].duration; - } - return slowTestTime; - } - - _allTestTime(result) { - let allTestTime = 0; - for (let i = 0; i < result.length; i++) { - allTestTime += result[i].duration; - } - return allTestTime; - } -} - -module.exports = JestSlowTestReporter; diff --git a/spec/test-utils/AccountDataAccumulator.ts b/spec/test-utils/AccountDataAccumulator.ts index 50d00973c5b..c18aaccfe4d 100644 --- a/spec/test-utils/AccountDataAccumulator.ts +++ b/spec/test-utils/AccountDataAccumulator.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import fetchMock from "fetch-mock-jest"; +import fetchMock from "@fetch-mock/vitest"; import { ISyncResponder } from "./SyncResponder"; diff --git a/spec/test-utils/E2EKeyReceiver.ts b/spec/test-utils/E2EKeyReceiver.ts index 24af06d6e37..add2ebe43b7 100644 --- a/spec/test-utils/E2EKeyReceiver.ts +++ b/spec/test-utils/E2EKeyReceiver.ts @@ -16,7 +16,7 @@ limitations under the License. import debugFunc from "debug"; import { Debugger } from "debug"; -import fetchMock from "fetch-mock-jest"; +import fetchMock from "@fetch-mock/vitest"; import type { IDeviceKeys, IOneTimeKey } from "../../src/@types/crypto"; diff --git a/spec/test-utils/E2EKeyResponder.ts b/spec/test-utils/E2EKeyResponder.ts index 57152f17621..169cfa24d49 100644 --- a/spec/test-utils/E2EKeyResponder.ts +++ b/spec/test-utils/E2EKeyResponder.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import fetchMock from "fetch-mock-jest"; +import fetchMock from "@fetch-mock/vitest"; import { MapWithDefault } from "../../src/utils"; import { IDownloadKeyResult } from "../../src"; diff --git a/spec/test-utils/SyncResponder.ts b/spec/test-utils/SyncResponder.ts index 3caba9dfa00..315e05d4959 100644 --- a/spec/test-utils/SyncResponder.ts +++ b/spec/test-utils/SyncResponder.ts @@ -16,7 +16,7 @@ limitations under the License. import debugFunc from "debug"; import { Debugger } from "debug"; -import fetchMock from "fetch-mock-jest"; +import fetchMock from "@fetch-mock/vitest"; import FetchMock from "fetch-mock"; /** Interface implemented by classes that intercept `/sync` requests from test clients diff --git a/spec/test-utils/client.ts b/spec/test-utils/client.ts index 62e11dffa13..0bd72ba10cb 100644 --- a/spec/test-utils/client.ts +++ b/spec/test-utils/client.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { MethodLikeKeys, mocked, MockedObject } from "jest-mock"; +import { MockedObject, vi } from "vitest"; import { ClientEventHandlerMap, EmittedEvents, MatrixClient } from "../../src/client"; import { TypedEventEmitter } from "../../src/models/typed-event-emitter"; @@ -26,7 +26,7 @@ import { User } from "../../src/models/user"; * to MatrixClient events */ export class MockClientWithEventEmitter extends TypedEventEmitter { - constructor(mockProperties: Partial, unknown>> = {}) { + constructor(mockProperties: Partial> = {}) { super(); Object.assign(this, mockProperties); } @@ -39,15 +39,14 @@ export class MockClientWithEventEmitter extends TypedEventEmitter, unknown>>, + mockProperties: Partial>, ): MockedObject => { - const mock = mocked(new MockClientWithEventEmitter(mockProperties) as unknown as MatrixClient); - return mock; + return new MockClientWithEventEmitter(mockProperties) as MockedObject; }; /** @@ -59,14 +58,14 @@ export const getMockClientWithEventEmitter = ( * ``` */ export const mockClientMethodsUser = (userId = "@alice:domain") => ({ - getUserId: jest.fn().mockReturnValue(userId), - getSafeUserId: jest.fn().mockReturnValue(userId), - getUser: jest.fn().mockReturnValue(new User(userId)), - isGuest: jest.fn().mockReturnValue(false), - mxcUrlToHttp: jest.fn().mockReturnValue("mock-mxcUrlToHttp"), + getUserId: vi.fn().mockReturnValue(userId), + getSafeUserId: vi.fn().mockReturnValue(userId), + getUser: vi.fn().mockReturnValue(new User(userId)), + isGuest: vi.fn().mockReturnValue(false), + mxcUrlToHttp: vi.fn().mockReturnValue("mock-mxcUrlToHttp"), credentials: { userId }, - getThreePids: jest.fn().mockResolvedValue({ threepids: [] }), - getAccessToken: jest.fn(), + getThreePids: vi.fn().mockResolvedValue({ threepids: [] }), + getAccessToken: vi.fn(), }); /** @@ -78,16 +77,16 @@ export const mockClientMethodsUser = (userId = "@alice:domain") => ({ * ``` */ export const mockClientMethodsEvents = () => ({ - decryptEventIfNeeded: jest.fn(), - getPushActionsForEvent: jest.fn(), + decryptEventIfNeeded: vi.fn(), + getPushActionsForEvent: vi.fn(), }); /** * Returns basic mocked client methods related to server support */ -export const mockClientMethodsServer = (): Partial, unknown>> => ({ - getIdentityServerUrl: jest.fn(), - getHomeserverUrl: jest.fn(), - getCachedCapabilities: jest.fn().mockReturnValue({}), - doesServerSupportUnstableFeature: jest.fn().mockResolvedValue(false), +export const mockClientMethodsServer = (): Partial> => ({ + getIdentityServerUrl: vi.fn(), + getHomeserverUrl: vi.fn(), + getCachedCapabilities: vi.fn().mockReturnValue({}), + doesServerSupportUnstableFeature: vi.fn().mockResolvedValue(false), }); diff --git a/spec/test-utils/emitter.ts b/spec/test-utils/emitter.ts index b56ac2ebc2b..6e340d017d4 100644 --- a/spec/test-utils/emitter.ts +++ b/spec/test-utils/emitter.ts @@ -18,7 +18,7 @@ limitations under the License. * Filter emitter.emit mock calls to find relevant events * eg: * ``` - * const emitSpy = jest.spyOn(state, 'emit'); + * const emitSpy = vi.spyOn(state, 'emit'); * << actions >> * const beaconLivenessEmits = emitCallsByEventType(BeaconEvent.New, emitSpy); * expect(beaconLivenessEmits.length).toBe(1); diff --git a/spec/test-utils/index.ts b/spec/test-utils/index.ts new file mode 100644 index 00000000000..19c64d226fd --- /dev/null +++ b/spec/test-utils/index.ts @@ -0,0 +1,22 @@ +/* +Copyright 2025 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { MockedFunction } from "vitest"; + +type Procedure = (...args: any[]) => any; +export function mocked(source: T): MockedFunction { + return source as MockedFunction; +} diff --git a/spec/test-utils/mockEndpoints.ts b/spec/test-utils/mockEndpoints.ts index 7c16583885e..49087d717b6 100644 --- a/spec/test-utils/mockEndpoints.ts +++ b/spec/test-utils/mockEndpoints.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import fetchMock from "fetch-mock-jest"; +import fetchMock from "@fetch-mock/vitest"; import { KeyBackupInfo } from "../../src/crypto-api"; diff --git a/spec/test-utils/test-utils.ts b/spec/test-utils/test-utils.ts index 4edc1161873..1552034b48b 100644 --- a/spec/test-utils/test-utils.ts +++ b/spec/test-utils/test-utils.ts @@ -1,4 +1,6 @@ // eslint-disable-next-line no-restricted-imports +import { vi } from "vitest"; + import EventEmitter from "events"; // load olm before the sdk if possible @@ -125,7 +127,7 @@ export function mock(constr: { new (...args: any[]): T }, name: string): T { // eslint-disable-line guard-for-in try { if (constr.prototype[key] instanceof Function) { - result[key] = jest.fn(); + result[key] = vi.fn(); } } catch { // Direct access to some non-function fields of DOM prototypes may @@ -593,7 +595,7 @@ export async function advanceTimersUntil(promise: Promise): Promise { }); while (!resolved) { - await jest.advanceTimersByTimeAsync(1); + await vi.advanceTimersByTimeAsync(1); } return await promise; diff --git a/spec/test-utils/thread.ts b/spec/test-utils/thread.ts index be91d9063c5..f527530c91f 100644 --- a/spec/test-utils/thread.ts +++ b/spec/test-utils/thread.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { expect } from "vitest"; + import { RelationType } from "../../src/@types/event"; import { MatrixClient } from "../../src/client"; import { MatrixEvent, MatrixEventEvent } from "../../src/models/event"; diff --git a/spec/test-utils/webrtc.ts b/spec/test-utils/webrtc.ts index 066bb09bc25..a691b674b61 100644 --- a/spec/test-utils/webrtc.ts +++ b/spec/test-utils/webrtc.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { vi } from "vitest"; + import { ClientEvent, ClientEventHandlerMap, @@ -269,7 +271,7 @@ export class MockRTCRtpTransceiver { this.peerConn.needsNegotiation = true; } - public setCodecPreferences = jest.fn(); + public setCodecPreferences = vi.fn(); } export class MockMediaStreamTrack { @@ -279,7 +281,7 @@ export class MockMediaStreamTrack { public enabled = true, ) {} - public stop = jest.fn(); + public stop = vi.fn(); public listeners: [string, (...args: any[]) => any][] = []; public isStopped = false; @@ -391,7 +393,7 @@ export class MockMediaHandler { public stopUserMediaStream(stream: MockMediaStream) { stream.isStopped = true; } - public getScreensharingStream = jest.fn((opts?: IScreensharingOpts) => { + public getScreensharingStream = vi.fn((opts?: IScreensharingOpts) => { const tracks = [new MockMediaStreamTrack("screenshare_video_track", "video")]; if (opts?.audio) tracks.push(new MockMediaStreamTrack("screenshare_audio_track", "audio")); @@ -416,18 +418,18 @@ export class MockMediaHandler { } export class MockMediaDevices { - public enumerateDevices = jest + public enumerateDevices = vi .fn, []>() .mockResolvedValue([ new MockMediaDeviceInfo("audioinput").typed(), new MockMediaDeviceInfo("videoinput").typed(), ]); - public getUserMedia = jest + public getUserMedia = vi .fn, [MediaStreamConstraints]>() .mockReturnValue(Promise.resolve(new MockMediaStream("local_stream").typed())); - public getDisplayMedia = jest + public getDisplayMedia = vi .fn, [MediaStreamConstraints]>() .mockReturnValue(Promise.resolve(new MockMediaStream("local_display_stream").typed())); @@ -462,11 +464,11 @@ export class MockCallMatrixClient extends TypedEventEmitter(), }; - public sendStateEvent = jest.fn< + public sendStateEvent = vi.fn< Promise, [roomId: string, eventType: EventType, content: any, statekey: string] >(); - public sendToDevice = jest.fn< + public sendToDevice = vi.fn< Promise, [eventType: string, contentMap: SendToDeviceContentMap, txnId?: string] >(); @@ -499,11 +501,11 @@ export class MockCallMatrixClient extends TypedEventEmitter false; public checkTurnServers = () => null; - public getSyncState = jest.fn().mockReturnValue(SyncState.Syncing); + public getSyncState = vi.fn().mockReturnValue(SyncState.Syncing); - public getRooms = jest.fn().mockReturnValue([]); - public getRoom = jest.fn(); - public getFoci = jest.fn(); + public getRooms = vi.fn().mockReturnValue([]); + public getRoom = vi.fn(); + public getFoci = vi.fn(); public supportsThreads(): boolean { return true; @@ -534,20 +536,20 @@ export class MockMatrixCall extends TypedEventEmitter(), - isAudioMuted: jest.fn().mockReturnValue(false), - isVideoMuted: jest.fn().mockReturnValue(false), + setAudioVideoMuted: vi.fn(), + isAudioMuted: vi.fn().mockReturnValue(false), + isVideoMuted: vi.fn().mockReturnValue(false), stream: new MockMediaStream("stream"), } as unknown as CallFeed; public remoteUsermediaFeed?: CallFeed; public remoteScreensharingFeed?: CallFeed; - public reject = jest.fn(); - public answerWithCallFeeds = jest.fn(); - public hangup = jest.fn(); - public initStats = jest.fn(); + public reject = vi.fn(); + public answerWithCallFeeds = vi.fn(); + public hangup = vi.fn(); + public initStats = vi.fn(); - public sendMetadataUpdate = jest.fn(); + public sendMetadataUpdate = vi.fn(); public getOpponentMember(): Partial { return this.opponentMember; @@ -586,40 +588,30 @@ export class MockCallFeed { } export function installWebRTCMocks() { - globalThis.navigator = { + vi.stubGlobal("navigator", { mediaDevices: new MockMediaDevices().typed(), - } as unknown as Navigator; + }); - globalThis.window = { - // @ts-ignore Mock + vi.stubGlobal("window", { RTCPeerConnection: MockRTCPeerConnection, - // @ts-ignore Mock RTCSessionDescription: {}, - // @ts-ignore Mock RTCIceCandidate: {}, getUserMedia: () => new MockMediaStream("local_stream"), - }; - // @ts-ignore Mock - globalThis.document = {}; - - // @ts-ignore Mock - globalThis.AudioContext = MockAudioContext; + }); - // @ts-ignore Mock - globalThis.RTCRtpReceiver = { - getCapabilities: jest.fn().mockReturnValue({ + vi.stubGlobal("AudioContext", MockAudioContext); + vi.stubGlobal("RTCRtpReceiver", { + getCapabilities: vi.fn().mockReturnValue({ codecs: [], headerExtensions: [], }), - }; - - // @ts-ignore Mock - globalThis.RTCRtpSender = { - getCapabilities: jest.fn().mockReturnValue({ + }); + vi.stubGlobal("RTCRtpSender", { + getCapabilities: vi.fn().mockReturnValue({ codecs: [], headerExtensions: [], }), - }; + }); } export function makeMockGroupCallStateEvent( @@ -632,22 +624,22 @@ export function makeMockGroupCallStateEvent( redacted?: boolean, ): MatrixEvent { return { - getType: jest.fn().mockReturnValue(EventType.GroupCallPrefix), - getRoomId: jest.fn().mockReturnValue(roomId), - getTs: jest.fn().mockReturnValue(0), - getContent: jest.fn().mockReturnValue(content), - getStateKey: jest.fn().mockReturnValue(groupCallId), - isRedacted: jest.fn().mockReturnValue(redacted ?? false), + getType: vi.fn().mockReturnValue(EventType.GroupCallPrefix), + getRoomId: vi.fn().mockReturnValue(roomId), + getTs: vi.fn().mockReturnValue(0), + getContent: vi.fn().mockReturnValue(content), + getStateKey: vi.fn().mockReturnValue(groupCallId), + isRedacted: vi.fn().mockReturnValue(redacted ?? false), } as unknown as MatrixEvent; } export function makeMockGroupCallMemberStateEvent(roomId: string, groupCallId: string): MatrixEvent { return { - getType: jest.fn().mockReturnValue(EventType.GroupCallMemberPrefix), - getRoomId: jest.fn().mockReturnValue(roomId), - getTs: jest.fn().mockReturnValue(0), - getContent: jest.fn().mockReturnValue({}), - getStateKey: jest.fn().mockReturnValue(groupCallId), + getType: vi.fn().mockReturnValue(EventType.GroupCallMemberPrefix), + getRoomId: vi.fn().mockReturnValue(roomId), + getTs: vi.fn().mockReturnValue(0), + getContent: vi.fn().mockReturnValue({}), + getStateKey: vi.fn().mockReturnValue(groupCallId), } as unknown as MatrixEvent; } diff --git a/spec/unit/NamespacedValue.spec.ts b/spec/unit/NamespacedValue.spec.ts index 0e659bee0d6..7fb85795a12 100644 --- a/spec/unit/NamespacedValue.spec.ts +++ b/spec/unit/NamespacedValue.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { describe, expect, it } from "vitest"; + import { NamespacedValue, UnstableValue } from "../../src/NamespacedValue"; describe("NamespacedValue", () => { diff --git a/spec/unit/ReEmitter.spec.ts b/spec/unit/ReEmitter.spec.ts index 9c4ea19255b..9707ef06e4d 100644 --- a/spec/unit/ReEmitter.spec.ts +++ b/spec/unit/ReEmitter.spec.ts @@ -15,6 +15,8 @@ limitations under the License. */ // eslint-disable-next-line no-restricted-imports +import { describe, expect, it, vi } from "vitest"; + import { EventEmitter } from "events"; import { ReEmitter } from "../../src/ReEmitter"; @@ -38,7 +40,7 @@ describe("ReEmitter", function () { const src = new EventSource(); const tgt = new EventTarget(); - const handler = jest.fn(); + const handler = vi.fn(); tgt.on(EVENTNAME, handler); const reEmitter = new ReEmitter(tgt); @@ -61,7 +63,7 @@ describe("ReEmitter", function () { // without the workaround in ReEmitter, this would throw src.doAnError(); - const handler = jest.fn(); + const handler = vi.fn(); tgt.on("error", handler); src.doAnError(); diff --git a/spec/unit/ToDeviceMessageQueue.spec.ts b/spec/unit/ToDeviceMessageQueue.spec.ts index 2689cd1e47d..bc216ea3b6b 100644 --- a/spec/unit/ToDeviceMessageQueue.spec.ts +++ b/spec/unit/ToDeviceMessageQueue.spec.ts @@ -1,3 +1,4 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; import { ConnectionError } from "../../src/http-api/errors"; import { ClientEvent, MatrixClient, Store } from "../../src/client"; import { ToDeviceMessageQueue } from "../../src/ToDeviceMessageQueue"; @@ -35,16 +36,16 @@ describe("onResumedSync", () => { }; store = new StubStore(); - store.getOldestToDeviceBatch = jest.fn().mockImplementation(() => { + store.getOldestToDeviceBatch = vi.fn().mockImplementation(() => { return batch; }); - store.removeToDeviceBatch = jest.fn().mockImplementation(() => { + store.removeToDeviceBatch = vi.fn().mockImplementation(() => { batch = null; }); mockClient = getMockClientWithEventEmitter({}); mockClient.store = store; - mockClient.sendToDevice = jest.fn().mockImplementation(async () => { + mockClient.sendToDevice = vi.fn().mockImplementation(async () => { if (shouldFailSendToDevice) { await Promise.reject(new ConnectionError("")).finally(() => { setTimeout(onSendToDeviceFailure, 0); diff --git a/spec/unit/autodiscovery.spec.ts b/spec/unit/autodiscovery.spec.ts index 282d2f57c1a..3a12db9d0f4 100644 --- a/spec/unit/autodiscovery.spec.ts +++ b/spec/unit/autodiscovery.spec.ts @@ -15,6 +15,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { afterAll, describe, expect, it } from "vitest"; + import MockHttpBackend from "matrix-mock-request"; import { AutoDiscoveryAction } from "../../src"; @@ -190,7 +192,7 @@ describe("AutoDiscovery", function () { }; return Promise.all([ httpBackend.flushAllExpected(), - AutoDiscovery.findClientConfig("example.org").then(expect(expected).toEqual), + AutoDiscovery.findClientConfig("example.org").then((config) => expect(expected).toEqual(config)), ]); }); diff --git a/spec/unit/base64.spec.ts b/spec/unit/base64.spec.ts index 69a64399d27..3d745bbb505 100644 --- a/spec/unit/base64.spec.ts +++ b/spec/unit/base64.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { describe, expect, it } from "vitest"; + import { TextEncoder, TextDecoder } from "util"; import { decodeBase64, encodeBase64, encodeUnpaddedBase64, encodeUnpaddedBase64Url } from "../../src/base64"; diff --git a/spec/unit/common-crypto/key-passphrase.spec.ts b/spec/unit/common-crypto/key-passphrase.spec.ts index 933b5f1834c..3c7b3b9db55 100644 --- a/spec/unit/common-crypto/key-passphrase.spec.ts +++ b/spec/unit/common-crypto/key-passphrase.spec.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +import { describe, expect, it } from "vitest"; + import { keyFromAuthData } from "../../../src/common-crypto/key-passphrase.ts"; describe("key-passphrase", () => { diff --git a/spec/unit/content-helpers.spec.ts b/spec/unit/content-helpers.spec.ts index 79c2f772606..8e6eaebe1c2 100644 --- a/spec/unit/content-helpers.spec.ts +++ b/spec/unit/content-helpers.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { afterAll, beforeEach, describe, expect, it, vi } from "vitest"; + import { LocationAssetType, M_ASSET, M_LOCATION, M_TIMESTAMP } from "../../src/@types/location"; import { M_TOPIC } from "../../src/@types/topic"; import { @@ -29,10 +31,10 @@ describe("Beacon content helpers", () => { describe("makeBeaconInfoContent()", () => { const mockDateNow = 123456789; beforeEach(() => { - jest.spyOn(globalThis.Date, "now").mockReturnValue(mockDateNow); + vi.spyOn(globalThis.Date, "now").mockReturnValue(mockDateNow); }); afterAll(() => { - jest.spyOn(globalThis.Date, "now").mockRestore(); + vi.spyOn(globalThis.Date, "now").mockRestore(); }); it("create fully defined event content", () => { expect(makeBeaconInfoContent(1234, true, "nice beacon_info", LocationAssetType.Pin)).toEqual({ diff --git a/spec/unit/content-repo.spec.ts b/spec/unit/content-repo.spec.ts index 8d24107fe26..1fe83399cb9 100644 --- a/spec/unit/content-repo.spec.ts +++ b/spec/unit/content-repo.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { describe, expect, it } from "vitest"; + import { getHttpUriForMxc } from "../../src/content-repo"; describe("ContentRepo", function () { diff --git a/spec/unit/crypto-api/recovery-key.spec.ts b/spec/unit/crypto-api/recovery-key.spec.ts index c488b7d06fe..e9f5508e001 100644 --- a/spec/unit/crypto-api/recovery-key.spec.ts +++ b/spec/unit/crypto-api/recovery-key.spec.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +import { describe, expect, it } from "vitest"; + import { decodeRecoveryKey, encodeRecoveryKey } from "../../../src/crypto-api"; describe("recovery key", () => { diff --git a/spec/unit/crypto.spec.ts b/spec/unit/crypto.spec.ts index 419bb530a66..b5cfccfe8db 100644 --- a/spec/unit/crypto.spec.ts +++ b/spec/unit/crypto.spec.ts @@ -1,3 +1,4 @@ +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import "../olm-loader"; // eslint-disable-next-line no-restricted-imports import { EventEmitter } from "events"; @@ -110,7 +111,7 @@ describe("Crypto", function () { }); afterEach(() => { - jest.useRealTimers(); + vi.useRealTimers(); }); it("Crypto exposes the correct olm library version", function () { @@ -386,10 +387,10 @@ describe("Crypto", function () { ); mockBaseApis = { - sendToDevice: jest.fn(), - getKeyBackupVersion: jest.fn(), - isGuest: jest.fn(), - emit: jest.fn(), + sendToDevice: vi.fn(), + getKeyBackupVersion: vi.fn(), + isGuest: vi.fn(), + emit: vi.fn(), } as unknown as MatrixClient; fakeEmitter = new EventEmitter(); @@ -412,16 +413,16 @@ describe("Crypto", function () { }); fakeEmitter.emit("toDeviceEvent", { - getId: jest.fn().mockReturnValue("$wedged"), - getType: jest.fn().mockReturnValue("m.room.message"), - getContent: jest.fn().mockReturnValue({ + getId: vi.fn().mockReturnValue("$wedged"), + getType: vi.fn().mockReturnValue("m.room.message"), + getContent: vi.fn().mockReturnValue({ msgtype: "m.bad.encrypted", }), - getWireContent: jest.fn().mockReturnValue({ + getWireContent: vi.fn().mockReturnValue({ algorithm: "m.olm.v1.curve25519-aes-sha2", sender_key: "this is a key", }), - getSender: jest.fn().mockReturnValue("@bob:home.server"), + getSender: vi.fn().mockReturnValue("@bob:home.server"), }); await prom; @@ -620,7 +621,7 @@ describe("Crypto", function () { const ksEvent = await keyshareEventForEvent(aliceClient, event, 1); ksEvent.getContent().sender_key = undefined; // test - bobClient.crypto!.olmDevice.addInboundGroupSession = jest.fn(); + bobClient.crypto!.olmDevice.addInboundGroupSession = vi.fn(); await bobDecryptor.onRoomKeyEvent(ksEvent); expect(bobClient.crypto!.olmDevice.addInboundGroupSession).not.toHaveBeenCalled(); }); @@ -649,7 +650,7 @@ describe("Crypto", function () { }); it("uses a new txnid for re-requesting keys", async function () { - jest.useFakeTimers(); + vi.useFakeTimers(); const event = new MatrixEvent({ sender: "@bob:example.com", @@ -661,7 +662,7 @@ describe("Crypto", function () { }, }); // replace Alice's sendToDevice function with a mock - const aliceSendToDevice = jest.fn().mockResolvedValue(undefined); + const aliceSendToDevice = vi.fn().mockResolvedValue(undefined); aliceClient.sendToDevice = aliceSendToDevice; aliceClient.startClient(); @@ -673,7 +674,7 @@ describe("Crypto", function () { // to force it to send now. // @ts-ignore aliceClient.crypto!.outgoingRoomKeyRequestManager.sendQueuedRequests(); - jest.runAllTimers(); + vi.runAllTimers(); await Promise.resolve(); expect(aliceSendToDevice).toHaveBeenCalledTimes(1); const txnId = aliceSendToDevice.mock.calls[0][2]; @@ -684,7 +685,7 @@ describe("Crypto", function () { // cancel and resend the room key request await aliceClient.cancelAndResendEventRoomKeyRequest(event); - jest.runAllTimers(); + vi.runAllTimers(); await Promise.resolve(); // cancelAndResend will call sendToDevice twice: // the first call to sendToDevice will be the cancellation @@ -1108,15 +1109,15 @@ describe("Crypto", function () { describe("Secret storage", function () { it("creates secret storage even if there is no keyInfo", async function () { - jest.spyOn(logger, "debug").mockImplementation(() => {}); - jest.setTimeout(10000); + vi.spyOn(logger, "debug").mockImplementation(() => {}); + vi.setTimeout(10000); const client = new TestClient("@a:example.com", "dev").client; await client.initLegacyCrypto(); client.crypto!.isCrossSigningReady = async () => false; - client.crypto!.baseApis.uploadDeviceSigningKeys = jest.fn().mockResolvedValue(null); - client.crypto!.baseApis.setAccountData = jest.fn().mockResolvedValue(null); - client.crypto!.baseApis.uploadKeySignatures = jest.fn(); - client.crypto!.baseApis.http.authedRequest = jest.fn(); + client.crypto!.baseApis.uploadDeviceSigningKeys = vi.fn().mockResolvedValue(null); + client.crypto!.baseApis.setAccountData = vi.fn().mockResolvedValue(null); + client.crypto!.baseApis.uploadKeySignatures = vi.fn(); + client.crypto!.baseApis.http.authedRequest = vi.fn(); const createSecretStorageKey = async () => { return { keyInfo: undefined, // Returning undefined here used to cause a crash @@ -1138,9 +1139,9 @@ describe("Crypto", function () { let encryptedPayload: object; beforeEach(async () => { - ensureOlmSessionsForDevices = jest.spyOn(olmlib, "ensureOlmSessionsForDevices"); + ensureOlmSessionsForDevices = vi.spyOn(olmlib, "ensureOlmSessionsForDevices"); ensureOlmSessionsForDevices.mockResolvedValue(new Map()); - encryptMessageForDevice = jest.spyOn(olmlib, "encryptMessageForDevice"); + encryptMessageForDevice = vi.spyOn(olmlib, "encryptMessageForDevice"); encryptMessageForDevice.mockImplementation(async (...[result, , , , , , payload]) => { result.plaintext = { type: 0, body: JSON.stringify(payload) }; }); @@ -1255,9 +1256,9 @@ describe("Crypto", function () { let crypto: Crypto; beforeEach(async () => { - ensureOlmSessionsForDevices = jest.spyOn(olmlib, "ensureOlmSessionsForDevices"); + ensureOlmSessionsForDevices = vi.spyOn(olmlib, "ensureOlmSessionsForDevices"); ensureOlmSessionsForDevices.mockResolvedValue(new Map()); - encryptMessageForDevice = jest.spyOn(olmlib, "encryptMessageForDevice"); + encryptMessageForDevice = vi.spyOn(olmlib, "encryptMessageForDevice"); encryptMessageForDevice.mockImplementation(async (...[result, , , , , , payload]) => { result.plaintext = { type: 0, body: JSON.stringify(payload) }; }); @@ -1294,7 +1295,7 @@ describe("Crypto", function () { ], ["@carol:example.org", new Map([["caroldesktop", new DeviceInfo("caroldesktop")]])], ]); - jest.spyOn(crypto.deviceList, "downloadKeys").mockResolvedValue(deviceInfoMap); + vi.spyOn(crypto.deviceList, "downloadKeys").mockResolvedValue(deviceInfoMap); // const deviceInfoMap = await this.downloadKeys(Array.from(userIds), false); const batch = await client.client.getCrypto()?.encryptToDeviceMessages( @@ -1342,7 +1343,7 @@ describe("Crypto", function () { }); it("returns empty batch if no devices known", async () => { - jest.spyOn(crypto.deviceList, "downloadKeys").mockResolvedValue(new Map()); + vi.spyOn(crypto.deviceList, "downloadKeys").mockResolvedValue(new Map()); const batch = await crypto.encryptToDeviceMessages( "m.test.type", [ @@ -1370,11 +1371,11 @@ describe("Crypto", function () { }); it("should free PkDecryption", () => { - const free = jest.fn(); - jest.spyOn(Olm, "PkDecryption").mockImplementation( + const free = vi.fn(); + vi.spyOn(Olm, "PkDecryption").mockImplementation( () => ({ - init_with_private_key: jest.fn(), + init_with_private_key: vi.fn(), free, }) as unknown as PkDecryption, ); @@ -1396,11 +1397,11 @@ describe("Crypto", function () { }); it("should free PkSigning", () => { - const free = jest.fn(); - jest.spyOn(Olm, "PkSigning").mockImplementation( + const free = vi.fn(); + vi.spyOn(Olm, "PkSigning").mockImplementation( () => ({ - init_with_seed: jest.fn(), + init_with_seed: vi.fn(), free, }) as unknown as PkSigning, ); @@ -1440,8 +1441,8 @@ describe("Crypto", function () { const cryptoStore = new MemoryCryptoStore(); mockRoomList = { - getRoomEncryption: jest.fn().mockReturnValue(null), - setRoomEncryption: jest.fn().mockResolvedValue(undefined), + getRoomEncryption: vi.fn().mockReturnValue(null), + setRoomEncryption: vi.fn().mockResolvedValue(undefined), } as unknown as RoomList; crypto = new Crypto(mockClient, "@alice:home.server", "FLIBBLE", clientStore, cryptoStore, []); @@ -1454,7 +1455,7 @@ describe("Crypto", function () { await clientStore.storeRoom(room); await crypto.setRoomEncryption("!room:id", { algorithm: "m.megolm.v1.aes-sha2" } as IRoomEncryption); expect(mockRoomList!.setRoomEncryption).toHaveBeenCalledTimes(1); - expect(jest.mocked(mockRoomList!.setRoomEncryption).mock.calls[0][0]).toEqual("!room:id"); + expect(vi.mocked(mockRoomList!.setRoomEncryption).mock.calls[0][0]).toEqual("!room:id"); }); it("should raise if called for an unknown room", async () => { diff --git a/spec/unit/crypto/CrossSigningInfo.spec.ts b/spec/unit/crypto/CrossSigningInfo.spec.ts index e99ceb2739b..68cee4cacf3 100644 --- a/spec/unit/crypto/CrossSigningInfo.spec.ts +++ b/spec/unit/crypto/CrossSigningInfo.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; + import "../../olm-loader"; import { CrossSigningInfo, createCryptoStoreCacheCallbacks } from "../../../src/crypto/CrossSigning"; import { IndexedDBCryptoStore } from "../../../src/crypto/store/indexeddb-crypto-store"; @@ -91,14 +93,14 @@ describe("CrossSigningInfo.getCrossSigningKey", function () { it.each(types)( "should request a key from the cache callback (if set)" + " and does not call app if one is found" + " %o", async ({ type, shouldCache }) => { - const getCrossSigningKey = jest.fn().mockImplementation(() => { + const getCrossSigningKey = vi.fn().mockImplementation(() => { if (shouldCache) { return Promise.reject(new Error("Regular callback called")); } else { return Promise.resolve(testKey); } }); - const getCrossSigningKeyCache = jest.fn().mockResolvedValue(testKey); + const getCrossSigningKeyCache = vi.fn().mockResolvedValue(testKey); const info = new CrossSigningInfo(userId, { getCrossSigningKey }, { getCrossSigningKeyCache }); const [pubKey] = await info.getCrossSigningKey(type, masterKeyPub); expect(pubKey).toEqual(masterKeyPub); @@ -111,8 +113,8 @@ describe("CrossSigningInfo.getCrossSigningKey", function () { ); it.each(types)("should store a key with the cache callback (if set)", async ({ type, shouldCache }) => { - const getCrossSigningKey = jest.fn().mockResolvedValue(testKey); - const storeCrossSigningKeyCache = jest.fn().mockResolvedValue(undefined); + const getCrossSigningKey = vi.fn().mockResolvedValue(testKey); + const storeCrossSigningKeyCache = vi.fn().mockResolvedValue(undefined); const info = new CrossSigningInfo(userId, { getCrossSigningKey }, { storeCrossSigningKeyCache }); const [pubKey] = await info.getCrossSigningKey(type, masterKeyPub); expect(pubKey).toEqual(masterKeyPub); @@ -124,23 +126,23 @@ describe("CrossSigningInfo.getCrossSigningKey", function () { }); it.each(types)("does not store a bad key to the cache", async ({ type, shouldCache }) => { - const getCrossSigningKey = jest.fn().mockResolvedValue(badKey); - const storeCrossSigningKeyCache = jest.fn().mockResolvedValue(undefined); + const getCrossSigningKey = vi.fn().mockResolvedValue(badKey); + const storeCrossSigningKeyCache = vi.fn().mockResolvedValue(undefined); const info = new CrossSigningInfo(userId, { getCrossSigningKey }, { storeCrossSigningKeyCache }); await expect(info.getCrossSigningKey(type, masterKeyPub)).rejects.toThrow(); expect(storeCrossSigningKeyCache.mock.calls.length).toEqual(0); }); it.each(types)("does not store a value to the cache if it came from the cache", async ({ type, shouldCache }) => { - const getCrossSigningKey = jest.fn().mockImplementation(() => { + const getCrossSigningKey = vi.fn().mockImplementation(() => { if (shouldCache) { return Promise.reject(new Error("Regular callback called")); } else { return Promise.resolve(testKey); } }); - const getCrossSigningKeyCache = jest.fn().mockResolvedValue(testKey); - const storeCrossSigningKeyCache = jest.fn().mockRejectedValue(new Error("Tried to store a value from cache")); + const getCrossSigningKeyCache = vi.fn().mockResolvedValue(testKey); + const storeCrossSigningKeyCache = vi.fn().mockRejectedValue(new Error("Tried to store a value from cache")); const info = new CrossSigningInfo( userId, { getCrossSigningKey }, @@ -154,9 +156,9 @@ describe("CrossSigningInfo.getCrossSigningKey", function () { it.each(types)( "requests a key from the cache callback (if set) and then calls app" + " if one is not found", async ({ type, shouldCache }) => { - const getCrossSigningKey = jest.fn().mockResolvedValue(testKey); - const getCrossSigningKeyCache = jest.fn().mockResolvedValue(undefined); - const storeCrossSigningKeyCache = jest.fn(); + const getCrossSigningKey = vi.fn().mockResolvedValue(testKey); + const getCrossSigningKeyCache = vi.fn().mockResolvedValue(undefined); + const storeCrossSigningKeyCache = vi.fn(); const info = new CrossSigningInfo( userId, { getCrossSigningKey }, @@ -175,9 +177,9 @@ describe("CrossSigningInfo.getCrossSigningKey", function () { it.each(types)( "requests a key from the cache callback (if set) and then" + " calls app if that key doesn't match", async ({ type, shouldCache }) => { - const getCrossSigningKey = jest.fn().mockResolvedValue(testKey); - const getCrossSigningKeyCache = jest.fn().mockResolvedValue(badKey); - const storeCrossSigningKeyCache = jest.fn(); + const getCrossSigningKey = vi.fn().mockResolvedValue(testKey); + const getCrossSigningKeyCache = vi.fn().mockResolvedValue(badKey); + const storeCrossSigningKeyCache = vi.fn(); const info = new CrossSigningInfo( userId, { getCrossSigningKey }, diff --git a/spec/unit/crypto/DeviceList.spec.ts b/spec/unit/crypto/DeviceList.spec.ts index 7d3ba5d3d44..cbfe5776eec 100644 --- a/spec/unit/crypto/DeviceList.spec.ts +++ b/spec/unit/crypto/DeviceList.spec.ts @@ -16,6 +16,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + import { logger } from "../../../src/logger"; import * as utils from "../../../src/utils"; import { MemoryCryptoStore } from "../../../src/crypto/store/memory-crypto-store"; @@ -86,7 +88,7 @@ describe("DeviceList", function () { beforeEach(function () { deviceLists = []; - downloadSpy = jest.fn(); + downloadSpy = vi.fn(); cryptoStore = new MemoryCryptoStore(); }); diff --git a/spec/unit/crypto/algorithms/megolm.spec.ts b/spec/unit/crypto/algorithms/megolm.spec.ts index 20d72702110..d7f9ee33b1c 100644 --- a/spec/unit/crypto/algorithms/megolm.spec.ts +++ b/spec/unit/crypto/algorithms/megolm.spec.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { mocked, MockedObject } from "jest-mock"; +import { beforeAll, beforeEach, describe, expect, it, MockedObject, vi } from "vitest"; import type { DeviceInfoMap } from "../../../../src/crypto/DeviceList"; import "../../../olm-loader"; @@ -37,6 +37,7 @@ import { MegolmEncryption as MegolmEncryptionClass } from "../../../../src/crypt import { recursiveMapToObject } from "../../../../src/utils"; import { sleep } from "../../../../src/utils"; import { KnownMembership } from "../../../../src/@types/membership"; +import { mocked } from "../../../test-utils"; const MegolmDecryption = algorithms.DECRYPTION_CLASSES.get("m.megolm.v1.aes-sha2")!; const MegolmEncryption = algorithms.ENCRYPTION_CLASSES.get("m.megolm.v1.aes-sha2")!; @@ -69,9 +70,9 @@ describe("MegolmDecryption", function () { }; mockBaseApis = { - claimOneTimeKeys: jest.fn(), - sendToDevice: jest.fn(), - queueToDevice: jest.fn(), + claimOneTimeKeys: vi.fn(), + sendToDevice: vi.fn(), + queueToDevice: vi.fn(), } as unknown as MockedObject; const cryptoStore = new MemoryCryptoStore(); @@ -88,14 +89,14 @@ describe("MegolmDecryption", function () { // we stub out the olm encryption bits mockOlmLib = { - encryptMessageForDevice: jest.fn().mockResolvedValue(undefined), - ensureOlmSessionsForDevices: jest.fn(), + encryptMessageForDevice: vi.fn().mockResolvedValue(undefined), + ensureOlmSessionsForDevices: vi.fn(), } as unknown as MockedObject; // @ts-ignore illegal assignment that makes these tests work :/ megolmDecryption.olmlib = mockOlmLib; - jest.clearAllMocks(); + vi.clearAllMocks(); }); describe("receives some keys:", function () { @@ -165,7 +166,7 @@ describe("MegolmDecryption", function () { it("can respond to a key request event", function () { const keyRequest: IncomingRoomKeyRequest = { requestId: "123", - share: jest.fn(), + share: vi.fn(), userId: "@alice:foo", deviceId: "alidevice", requestBody: { @@ -263,8 +264,8 @@ describe("MegolmDecryption", function () { origin_server_ts: 1507753886000, }); - const successHandler = jest.fn(); - const failureHandler = jest.fn((err) => { + const successHandler = vi.fn(); + const failureHandler = vi.fn((err) => { expect(err.toString()).toMatch(/Duplicate message index, possible replay attack/); }); @@ -335,7 +336,7 @@ describe("MegolmDecryption", function () { const cryptoStore = new MemoryCryptoStore(); olmDevice = new OlmDevice(cryptoStore); - olmDevice.verifySignature = jest.fn(); + olmDevice.verifySignature = vi.fn(); await olmDevice.init(); mockBaseApis.claimOneTimeKeys.mockResolvedValue({ @@ -360,10 +361,10 @@ describe("MegolmDecryption", function () { aliceDeviceInfo = { deviceId: "aliceDevice", - isBlocked: jest.fn().mockReturnValue(false), - isUnverified: jest.fn().mockReturnValue(false), - getIdentityKey: jest.fn().mockReturnValue("YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWE"), - getFingerprint: jest.fn().mockReturnValue(""), + isBlocked: vi.fn().mockReturnValue(false), + isUnverified: vi.fn().mockReturnValue(false), + getIdentityKey: vi.fn().mockReturnValue("YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWE"), + getFingerprint: vi.fn().mockReturnValue(""), } as unknown as DeviceInfo; mockCrypto.downloadKeys.mockReturnValue( @@ -398,9 +399,9 @@ describe("MegolmDecryption", function () { mockRoom = { roomId: ROOM_ID, - getEncryptionTargetMembers: jest.fn().mockReturnValue([{ userId: "@alice:home.server" }]), - getBlacklistUnverifiedDevices: jest.fn().mockReturnValue(false), - shouldEncryptForInvitedMembers: jest.fn().mockReturnValue(false), + getEncryptionTargetMembers: vi.fn().mockReturnValue([{ userId: "@alice:home.server" }]), + getBlacklistUnverifiedDevices: vi.fn().mockReturnValue(false), + shouldEncryptForInvitedMembers: vi.fn().mockReturnValue(false), } as unknown as Room; }); @@ -427,7 +428,7 @@ describe("MegolmDecryption", function () { megolmEncryption.setupPromise = Promise.resolve(session); // @ts-ignore - private method access - const prepareNewSessionSpy = jest.spyOn(megolmEncryption, "prepareNewSession"); + const prepareNewSessionSpy = vi.spyOn(megolmEncryption, "prepareNewSession"); await megolmEncryption.encryptMessage(mockRoom, "a.fake.type", { body: "Some text", }); @@ -488,9 +489,7 @@ describe("MegolmDecryption", function () { body: "Some text", }); - aliceDeviceInfo.getIdentityKey = jest - .fn() - .mockReturnValue("YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWI"); + aliceDeviceInfo.getIdentityKey = vi.fn().mockReturnValue("YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWI"); mockBaseApis.queueToDevice.mockClear(); await megolmEncryption.reshareKeyWithDevice!( @@ -527,7 +526,7 @@ describe("MegolmDecryption", function () { describe("prepareToEncrypt", () => { let megolm: MegolmEncryptionClass; - let room: jest.Mocked; + let room: MockedObject; const deviceMap: DeviceInfoMap = new Map([ [ @@ -557,7 +556,7 @@ describe("MegolmDecryption", function () { ]); beforeEach(() => { - room = testUtils.mock(Room, "Room") as jest.Mocked; + room = testUtils.mock(Room, "Room") as MockedObject; room.getEncryptionTargetMembers.mockImplementation(async () => [ new RoomMember(room.roomId, "@user:example.org"), ]); @@ -660,7 +659,7 @@ describe("MegolmDecryption", function () { return this.getDevicesFromStore(userIds); }; - aliceClient.sendToDevice = jest.fn().mockResolvedValue({}); + aliceClient.sendToDevice = vi.fn().mockResolvedValue({}); const event = new MatrixEvent({ type: "m.room.message", @@ -758,7 +757,7 @@ describe("MegolmDecryption", function () { } } - aliceClient.claimOneTimeKeys = jest.fn().mockResolvedValue({ + aliceClient.claimOneTimeKeys = vi.fn().mockResolvedValue({ one_time_keys: { "@bob:example.com": { bobdevice: signedOneTimeKeys, @@ -767,7 +766,7 @@ describe("MegolmDecryption", function () { failures: {}, }); - aliceClient.sendToDevice = jest.fn().mockResolvedValue({}); + aliceClient.sendToDevice = vi.fn().mockResolvedValue({}); const event = new MatrixEvent({ type: "m.key.verification.start", @@ -808,7 +807,7 @@ describe("MegolmDecryption", function () { await aliceClient.setRoomEncryption(roomId, encryptionCfg); await bobClient.setRoomEncryption(roomId, encryptionCfg); - aliceRoom.getEncryptionTargetMembers = jest.fn().mockResolvedValue([ + aliceRoom.getEncryptionTargetMembers = vi.fn().mockResolvedValue([ { userId: "@alice:example.com", membership: KnownMembership.Join, @@ -838,13 +837,13 @@ describe("MegolmDecryption", function () { return this.getDevicesFromStore(userIds); }; - aliceClient.claimOneTimeKeys = jest.fn().mockResolvedValue({ + aliceClient.claimOneTimeKeys = vi.fn().mockResolvedValue({ // Bob has no one-time keys one_time_keys: {}, failures: {}, }); - aliceClient.sendToDevice = jest.fn().mockResolvedValue({}); + aliceClient.sendToDevice = vi.fn().mockResolvedValue({}); const event = new MatrixEvent({ type: "m.room.message", @@ -964,7 +963,7 @@ describe("MegolmDecryption", function () { const aliceEventEmitter = new TypedEventEmitter(); aliceClient.crypto!.registerEventHandlers(aliceEventEmitter); - aliceClient.crypto!.downloadKeys = jest.fn(); + aliceClient.crypto!.downloadKeys = vi.fn(); const bobDevice = bobClient.crypto!.olmDevice; const roomId = "!someroom"; @@ -1060,7 +1059,7 @@ describe("MegolmDecryption", function () { aliceClient.crypto!.registerEventHandlers(aliceEventEmitter); const bobDevice = bobClient.crypto!.olmDevice; - aliceClient.crypto!.downloadKeys = jest.fn(); + aliceClient.crypto!.downloadKeys = vi.fn(); const roomId = "!someroom"; diff --git a/spec/unit/crypto/algorithms/olm.spec.ts b/spec/unit/crypto/algorithms/olm.spec.ts index 6c50f09635e..c11a1a51f44 100644 --- a/spec/unit/crypto/algorithms/olm.spec.ts +++ b/spec/unit/crypto/algorithms/olm.spec.ts @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { MockedObject } from "jest-mock"; +import { beforeAll, beforeEach, describe, expect, it, MockedObject } from "vitest"; import "../../../olm-loader"; import { MemoryCryptoStore } from "../../../../src/crypto/store/memory-crypto-store"; diff --git a/spec/unit/crypto/backup.spec.ts b/spec/unit/crypto/backup.spec.ts index c210d14c80b..b8de66f57d6 100644 --- a/spec/unit/crypto/backup.spec.ts +++ b/spec/unit/crypto/backup.spec.ts @@ -15,6 +15,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { afterEach, beforeAll, beforeEach, describe, expect, it, test, vi } from "vitest"; + import "../../olm-loader"; import { logger } from "../../../src/logger"; import * as olmlib from "../../../src/crypto/olmlib"; @@ -125,7 +127,7 @@ function saveCrossSigningKeys(k: Record) { function makeTestScheduler(): MatrixScheduler { return (["getQueueForEvent", "queueEvent", "removeEventFromQueue", "setProcessFunction"] as const).reduce( (r, k) => { - r[k] = jest.fn(); + r[k] = vi.fn(); return r; }, {} as MatrixScheduler, @@ -140,7 +142,7 @@ function makeTestClient(cryptoStore: CryptoStore) { baseUrl: "https://my.home.server", idBaseUrl: "https://identity.server", accessToken: "my.access.token", - fetchFn: jest.fn(), // NOP + fetchFn: vi.fn(), // NOP store: store, scheduler: scheduler, userId: "@alice:bar", @@ -150,7 +152,7 @@ function makeTestClient(cryptoStore: CryptoStore) { }); // initialising the crypto library will trigger a key upload request, which we can stub out - client.uploadKeysRequest = jest.fn(); + client.uploadKeysRequest = vi.fn(); return client; } @@ -181,8 +183,8 @@ describe("MegolmBackup", function () { // we stub out the olm encryption bits mockOlmLib = {} as unknown as typeof olmlib; - mockOlmLib.ensureOlmSessionsForDevices = jest.fn(); - mockOlmLib.encryptMessageForDevice = jest.fn().mockResolvedValue(undefined); + mockOlmLib.ensureOlmSessionsForDevices = vi.fn(); + mockOlmLib.encryptMessageForDevice = vi.fn().mockResolvedValue(undefined); }); describe("backup", function () { @@ -206,13 +208,13 @@ describe("MegolmBackup", function () { // ideally we would use lolex, but we have no oportunity // to tick the clock between the first try and the retry. const realSetTimeout = globalThis.setTimeout; - jest.spyOn(globalThis, "setTimeout").mockImplementation(function (f, n) { + vi.spyOn(globalThis, "setTimeout").mockImplementation(function (f, n) { return realSetTimeout(f!, n! / 100); }); }); afterEach(function () { - jest.spyOn(globalThis, "setTimeout").mockRestore(); + vi.spyOn(globalThis, "setTimeout").mockRestore(); }); test("fail if crypto not enabled", async () => { @@ -281,7 +283,7 @@ describe("MegolmBackup", function () { // @ts-ignore readonly field write mockCrypto.backupManager = { - backupGroupSession: jest.fn(), + backupGroupSession: vi.fn(), }; return event @@ -539,7 +541,7 @@ describe("MegolmBackup", function () { baseUrl: "https://my.home.server", idBaseUrl: "https://identity.server", accessToken: "my.access.token", - fetchFn: jest.fn(), // NOP + fetchFn: vi.fn(), // NOP store: store, scheduler: scheduler, userId: "@alice:bar", @@ -547,7 +549,7 @@ describe("MegolmBackup", function () { cryptoStore: cryptoStore, }); // initialising the crypto library will trigger a key upload request, which we can stub out - client.uploadKeysRequest = jest.fn(); + client.uploadKeysRequest = vi.fn(); megolmDecryption = new MegolmDecryption({ userId: "@user:id", @@ -763,7 +765,7 @@ describe("MegolmBackup", function () { baseUrl: "https://my.home.server", idBaseUrl: "https://identity.server", accessToken: "my.access.token", - fetchFn: jest.fn(), // NOP + fetchFn: vi.fn(), // NOP store, scheduler, userId: "@alice:bar", @@ -771,11 +773,11 @@ describe("MegolmBackup", function () { cryptoStore, }); // initialising the crypto library will trigger a key upload request, which we can stub out - client.uploadKeysRequest = jest.fn(); + client.uploadKeysRequest = vi.fn(); await client.initLegacyCrypto(); - cryptoStore.countSessionsNeedingBackup = jest.fn().mockReturnValue(6); + cryptoStore.countSessionsNeedingBackup = vi.fn().mockReturnValue(6); await expect(client.flagAllGroupSessionsForBackup()).resolves.toBe(6); client.stopClient(); }); diff --git a/spec/unit/crypto/cross-signing.spec.ts b/spec/unit/crypto/cross-signing.spec.ts index a8b7fa2624b..668bd743e56 100644 --- a/spec/unit/crypto/cross-signing.spec.ts +++ b/spec/unit/crypto/cross-signing.spec.ts @@ -95,7 +95,7 @@ describe("Cross Signing", function () { it("should sign the master key with the device key", async function () { const { client: alice } = await makeTestClient({ userId: "@alice:example.com", deviceId: "Osborne2" }); - alice.uploadDeviceSigningKeys = jest.fn().mockImplementation(async (auth, keys) => { + alice.uploadDeviceSigningKeys = vi.fn().mockImplementation(async (auth, keys) => { await olmlib.verifySignature( alice.crypto!.olmDevice, keys.master_key, @@ -234,7 +234,7 @@ describe("Cross Signing", function () { }); const uploadSigsPromise = new Promise((resolve, reject) => { - alice.uploadKeySignatures = jest.fn().mockImplementation(async (content) => { + alice.uploadKeySignatures = vi.fn().mockImplementation(async (content) => { try { await olmlib.verifySignature( alice.crypto!.olmDevice, diff --git a/spec/unit/crypto/dehydration.spec.ts b/spec/unit/crypto/dehydration.spec.ts index d9a0dac895e..2f14ed684cb 100644 --- a/spec/unit/crypto/dehydration.spec.ts +++ b/spec/unit/crypto/dehydration.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { beforeAll, describe, expect, it } from "vitest"; + import "../../olm-loader"; import { TestClient } from "../../TestClient"; import { logger } from "../../../src/logger"; diff --git a/spec/unit/crypto/device-converter.spec.ts b/spec/unit/crypto/device-converter.spec.ts index d54f8f4e7ca..b216062c39b 100644 --- a/spec/unit/crypto/device-converter.spec.ts +++ b/spec/unit/crypto/device-converter.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { describe, expect, it } from "vitest"; + import { DeviceInfo } from "../../../src/crypto/deviceinfo"; import { DeviceVerification } from "../../../src"; import { deviceInfoToDevice } from "../../../src/crypto/device-converter"; diff --git a/spec/unit/crypto/outgoing-room-key-requests.spec.ts b/spec/unit/crypto/outgoing-room-key-requests.spec.ts index f7d5160610a..53fcc093785 100644 --- a/spec/unit/crypto/outgoing-room-key-requests.spec.ts +++ b/spec/unit/crypto/outgoing-room-key-requests.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { beforeAll, describe, expect, it, test } from "vitest"; + import { CryptoStore } from "../../../src/crypto/store/base"; import { IndexedDBCryptoStore } from "../../../src/crypto/store/indexeddb-crypto-store"; import { LocalStorageCryptoStore } from "../../../src/crypto/store/localStorage-crypto-store"; diff --git a/spec/unit/crypto/secrets.spec.ts b/spec/unit/crypto/secrets.spec.ts index 097ee2b1b19..834fcc18f70 100644 --- a/spec/unit/crypto/secrets.spec.ts +++ b/spec/unit/crypto/secrets.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { beforeAll, describe, expect, it, vi } from "vitest"; + import "../../olm-loader"; import * as olmlib from "../../../src/crypto/olmlib"; import { IObject } from "../../../src/crypto/olmlib"; @@ -47,7 +49,7 @@ async function makeTestClient( await client.initLegacyCrypto(); // No need to download keys for these tests - jest.spyOn(client.crypto!, "downloadKeys").mockResolvedValue(new Map()); + vi.spyOn(client.crypto!, "downloadKeys").mockResolvedValue(new Map()); return client; } @@ -101,7 +103,7 @@ describe("Secrets", function () { }, }; - const getKey = jest.fn().mockImplementation(async (e) => { + const getKey = vi.fn().mockImplementation(async (e) => { expect(Object.keys(e.keys)).toEqual(["abc"]); return ["abc", key]; }); @@ -121,7 +123,7 @@ describe("Secrets", function () { const secretStorage = alice.crypto!.secretStorage; - jest.spyOn(alice, "setAccountData").mockImplementation(async function (eventType, contents) { + vi.spyOn(alice, "setAccountData").mockImplementation(async function (eventType, contents) { alice.store.storeAccountDataEvents([ new MatrixEvent({ type: eventType, @@ -171,7 +173,7 @@ describe("Secrets", function () { it("should encrypt with default key if keys is null", async function () { const key = new Uint8Array(16); for (let i = 0; i < 16; i++) key[i] = i; - const getKey = jest.fn().mockImplementation(async (e) => { + const getKey = vi.fn().mockImplementation(async (e) => { expect(Object.keys(e.keys)).toEqual([newKeyId]); return [newKeyId, key]; }); @@ -292,7 +294,7 @@ describe("Secrets", function () { it("bootstraps when no storage or cross-signing keys locally", async function () { const key = new Uint8Array(16); for (let i = 0; i < 16; i++) key[i] = i; - const getKey = jest.fn().mockImplementation(async (e) => { + const getKey = vi.fn().mockImplementation(async (e) => { return [Object.keys(e.keys)[0], key]; }); @@ -308,7 +310,7 @@ describe("Secrets", function () { }, ); bob.uploadDeviceSigningKeys = async () => ({}); - bob.uploadKeySignatures = jest.fn().mockResolvedValue(undefined); + bob.uploadKeySignatures = vi.fn().mockResolvedValue(undefined); bob.setAccountData = async function (eventType, contents) { const event = new MatrixEvent({ type: eventType, @@ -318,7 +320,7 @@ describe("Secrets", function () { this.emit(ClientEvent.AccountData, event); return {}; }; - bob.getKeyBackupVersion = jest.fn().mockResolvedValue(null); + bob.getKeyBackupVersion = vi.fn().mockResolvedValue(null); await bob.bootstrapCrossSigning({ authUploadDeviceSigningKeys: async (func) => { diff --git a/spec/unit/crypto/store/CryptoStore.spec.ts b/spec/unit/crypto/store/CryptoStore.spec.ts index f62c17dacb1..a6a281a0866 100644 --- a/spec/unit/crypto/store/CryptoStore.spec.ts +++ b/spec/unit/crypto/store/CryptoStore.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { beforeEach, describe, expect, it } from "vitest"; + import "fake-indexeddb/auto"; import "jest-localstorage-mock"; import { IndexedDBCryptoStore, LocalStorageCryptoStore, MemoryCryptoStore } from "../../../../src"; diff --git a/spec/unit/crypto/store/IndexedDBCryptoStore.spec.ts b/spec/unit/crypto/store/IndexedDBCryptoStore.spec.ts index 21523c9107a..b27f0c7778e 100644 --- a/spec/unit/crypto/store/IndexedDBCryptoStore.spec.ts +++ b/spec/unit/crypto/store/IndexedDBCryptoStore.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { beforeEach, describe, expect, it } from "vitest"; + import "fake-indexeddb/auto"; import { IndexedDBCryptoStore } from "../../../../src"; import { MigrationState } from "../../../../src/crypto/store/base"; diff --git a/spec/unit/crypto/verification/InRoomChannel.spec.ts b/spec/unit/crypto/verification/InRoomChannel.spec.ts index 562c83b04a3..a7d8c105fdc 100644 --- a/spec/unit/crypto/verification/InRoomChannel.spec.ts +++ b/spec/unit/crypto/verification/InRoomChannel.spec.ts @@ -13,6 +13,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ +import { describe, expect, it } from "vitest"; + import { MatrixClient } from "../../../../src/client"; import { InRoomChannel } from "../../../../src/crypto/verification/request/InRoomChannel"; import { MatrixEvent } from "../../../../src/models/event"; diff --git a/spec/unit/crypto/verification/qr_code.spec.ts b/spec/unit/crypto/verification/qr_code.spec.ts index 0f8fdcba591..e189057092e 100644 --- a/spec/unit/crypto/verification/qr_code.spec.ts +++ b/spec/unit/crypto/verification/qr_code.spec.ts @@ -14,6 +14,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ +import { beforeAll, describe, it } from "vitest"; + import "../../../olm-loader"; import { logger } from "../../../../src/logger"; diff --git a/spec/unit/crypto/verification/request.spec.ts b/spec/unit/crypto/verification/request.spec.ts index c3b45b7b813..91a80304f98 100644 --- a/spec/unit/crypto/verification/request.spec.ts +++ b/spec/unit/crypto/verification/request.spec.ts @@ -14,6 +14,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ +import { beforeAll, describe, expect, it, vi } from "vitest"; + import "../../../olm-loader"; import { CryptoEvent, verificationMethods } from "../../../../src/crypto"; import { logger } from "../../../../src/logger"; @@ -22,7 +24,7 @@ import { makeTestClients } from "./util"; const Olm = globalThis.Olm; -jest.useFakeTimers(); +vi.useFakeTimers(); describe("verification request integration tests with crypto layer", function () { if (!globalThis.Olm) { @@ -56,8 +58,8 @@ describe("verification request integration tests with crypto layer", function () }, }; }; - alice.client.downloadKeys = jest.fn().mockResolvedValue({}); - bob.client.downloadKeys = jest.fn().mockResolvedValue({}); + alice.client.downloadKeys = vi.fn().mockResolvedValue({}); + bob.client.downloadKeys = vi.fn().mockResolvedValue({}); bob.client.on(CryptoEvent.VerificationRequest, (request) => { const bobVerifier = request.beginKeyVerification(verificationMethods.SAS); bobVerifier.verify(); diff --git a/spec/unit/crypto/verification/sas.spec.ts b/spec/unit/crypto/verification/sas.spec.ts index 939dc3b7789..573eb8ed9cc 100644 --- a/spec/unit/crypto/verification/sas.spec.ts +++ b/spec/unit/crypto/verification/sas.spec.ts @@ -64,7 +64,7 @@ describe("SAS verification", function () { content: {}, }), ); - const spy = jest.fn(); + const spy = vi.fn(); await sas.verify().catch(spy); expect(spy).toHaveBeenCalled(); @@ -411,10 +411,10 @@ describe("SAS verification", function () { verificationMethods: [verificationMethods.SAS], }, ); - alice.client.setDeviceVerified = jest.fn(); - alice.client.downloadKeys = jest.fn().mockResolvedValue({}); - bob.client.setDeviceVerified = jest.fn(); - bob.client.downloadKeys = jest.fn().mockResolvedValue({}); + alice.client.setDeviceVerified = vi.fn(); + alice.client.downloadKeys = vi.fn().mockResolvedValue({}); + bob.client.setDeviceVerified = vi.fn(); + bob.client.downloadKeys = vi.fn().mockResolvedValue({}); const bobPromise = new Promise>((resolve, reject) => { bob.client.on(CryptoEvent.VerificationRequest, (request) => { @@ -431,8 +431,8 @@ describe("SAS verification", function () { bob.client.deviceId!, ); - const aliceSpy = jest.fn(); - const bobSpy = jest.fn(); + const aliceSpy = vi.fn(); + const bobSpy = vi.fn(); await Promise.all([ aliceVerifier.verify().catch(aliceSpy), bobPromise.then((verifier) => verifier.verify()).catch(bobSpy), @@ -467,7 +467,7 @@ describe("SAS verification", function () { }, ); - alice.client.crypto!.setDeviceVerification = jest.fn(); + alice.client.crypto!.setDeviceVerification = vi.fn(); alice.client.getDeviceEd25519Key = () => { return "alice+base64+ed25519+key"; }; @@ -485,7 +485,7 @@ describe("SAS verification", function () { return Promise.resolve(new Map()); }; - bob.client.crypto!.setDeviceVerification = jest.fn(); + bob.client.crypto!.setDeviceVerification = vi.fn(); bob.client.getStoredDevice = () => { return DeviceInfo.fromStorage( { diff --git a/spec/unit/crypto/verification/secret_request.spec.ts b/spec/unit/crypto/verification/secret_request.spec.ts index acc00abf120..f851c38cef3 100644 --- a/spec/unit/crypto/verification/secret_request.spec.ts +++ b/spec/unit/crypto/verification/secret_request.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { beforeAll, describe, expect, it, vi } from "vitest"; + import "../../../olm-loader"; import { MatrixClient, MatrixEvent } from "../../../../src/matrix"; import { encodeBase64 } from "../../../../src/base64"; @@ -23,7 +25,7 @@ import { VerificationRequest } from "../../../../src/crypto/verification/request import { IVerificationChannel } from "../../../../src/crypto/verification/request/Channel"; import { VerificationBase } from "../../../../src/crypto/verification/Base"; -jest.useFakeTimers(); +vi.useFakeTimers(); // Private key for tests only const testKey = new Uint8Array([ @@ -41,8 +43,8 @@ describe("self-verifications", () => { const userId = "@test:localhost"; const cacheCallbacks = { - getCrossSigningKeyCache: jest.fn().mockReturnValue(null), - storeCrossSigningKeyCache: jest.fn(), + getCrossSigningKeyCache: vi.fn().mockReturnValue(null), + storeCrossSigningKeyCache: vi.fn(), }; const crossSigningInfo = new CrossSigningInfo(userId, {}, cacheCallbacks); @@ -65,13 +67,13 @@ describe("self-verifications", () => { }; const secretStorage = { - request: jest.fn().mockReturnValue({ + request: vi.fn().mockReturnValue({ promise: Promise.resolve(encodeBase64(testKey)), }), }; - const storeSessionBackupPrivateKey = jest.fn(); - const restoreKeyBackupWithCache = jest.fn(() => Promise.resolve()); + const storeSessionBackupPrivateKey = vi.fn(); + const restoreKeyBackupWithCache = vi.fn(() => Promise.resolve()); const client = { crypto: { diff --git a/spec/unit/crypto/verification/verification_request.spec.ts b/spec/unit/crypto/verification/verification_request.spec.ts index 2f42e54d8ca..3f3a177048e 100644 --- a/spec/unit/crypto/verification/verification_request.spec.ts +++ b/spec/unit/crypto/verification/verification_request.spec.ts @@ -13,12 +13,9 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -import { - VerificationRequest, - READY_TYPE, - START_TYPE, - DONE_TYPE, -} from "../../../../src/crypto/verification/request/VerificationRequest"; +import { describe, expect, it, vi } from "vitest"; + +import { VerificationRequest, READY_TYPE, START_TYPE, DONE_TYPE } from "../../../../src/crypto/verification/request/VerificationRequest"; import { InRoomChannel } from "../../../../src/crypto/verification/request/InRoomChannel"; import { ToDeviceChannel } from "../../../../src/crypto/verification/request/ToDeviceChannel"; import { IContent, MatrixEvent } from "../../../../src/models/event"; @@ -144,7 +141,7 @@ async function distributeEvent( await theirRequest.channel.handleEvent(event, theirRequest, true); } -jest.useFakeTimers(); +vi.useFakeTimers(); describe("verification request unit tests", function () { it("transition from UNSENT to DONE through happy path", async function () { @@ -305,7 +302,7 @@ describe("verification request unit tests", function () { expect(aliceRequest.cancelled).toBe(false); expect(aliceRequest._cancellingUserId).toBe(undefined); - jest.advanceTimersByTime(10 * 60 * 1000); + vi.advanceTimersByTime(10 * 60 * 1000); expect(aliceRequest._cancellingUserId).toBe(alice.getUserId()); }); @@ -325,7 +322,7 @@ describe("verification request unit tests", function () { expect(bobRequest.cancelled).toBe(false); expect(bobRequest._cancellingUserId).toBe(undefined); - jest.advanceTimersByTime(2 * 60 * 1000); + vi.advanceTimersByTime(2 * 60 * 1000); expect(bobRequest._cancellingUserId).toBe(bob.getUserId()); }); }); diff --git a/spec/unit/digest.spec.ts b/spec/unit/digest.spec.ts index 4d33564f73e..2a210a722b8 100644 --- a/spec/unit/digest.spec.ts +++ b/spec/unit/digest.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { describe, expect, it, vi } from "vitest"; + import { encodeUnpaddedBase64Url } from "../../src"; import { sha256 } from "../../src/digest"; @@ -29,12 +31,7 @@ describe("sha256", () => { }); it("throws if webcrypto is not available", async () => { - const oldCrypto = globalThis.crypto; - try { - globalThis.crypto = {} as any; - await expect(sha256("test")).rejects.toThrow(); - } finally { - globalThis.crypto = oldCrypto; - } + vi.stubGlobal("crypto", {}); + await expect(sha256("test")).rejects.toThrow(); }); }); diff --git a/spec/unit/embedded.spec.ts b/spec/unit/embedded.spec.ts index c5ef3a6a2c6..379c75b86d2 100644 --- a/spec/unit/embedded.spec.ts +++ b/spec/unit/embedded.spec.ts @@ -1,5 +1,5 @@ /** - * @jest-environment jsdom + * @vitest-environment jsdom */ /* @@ -21,8 +21,9 @@ limitations under the License. // We have to use EventEmitter here to mock part of the matrix-widget-api // project, which doesn't know about our TypeEventEmitter implementation at all // eslint-disable-next-line no-restricted-imports +import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, MockedObject, vi } from "vitest"; + import { EventEmitter } from "events"; -import { MockedObject } from "jest-mock"; import { WidgetApi, WidgetApiToWidgetAction, @@ -50,25 +51,25 @@ const testOIDCToken = { token_type: "Bearer", }; class MockWidgetApi extends EventEmitter { - public start = jest.fn(); - public requestCapability = jest.fn(); - public requestCapabilities = jest.fn(); - public requestCapabilityForRoomTimeline = jest.fn(); - public requestCapabilityToSendEvent = jest.fn(); - public requestCapabilityToReceiveEvent = jest.fn(); - public requestCapabilityToSendMessage = jest.fn(); - public requestCapabilityToReceiveMessage = jest.fn(); - public requestCapabilityToSendState = jest.fn(); - public requestCapabilityToReceiveState = jest.fn(); - public requestCapabilityToSendToDevice = jest.fn(); - public requestCapabilityToReceiveToDevice = jest.fn(); - public sendRoomEvent = jest.fn( + public start = vi.fn(); + public requestCapability = vi.fn(); + public requestCapabilities = vi.fn(); + public requestCapabilityForRoomTimeline = vi.fn(); + public requestCapabilityToSendEvent = vi.fn(); + public requestCapabilityToReceiveEvent = vi.fn(); + public requestCapabilityToSendMessage = vi.fn(); + public requestCapabilityToReceiveMessage = vi.fn(); + public requestCapabilityToSendState = vi.fn(); + public requestCapabilityToReceiveState = vi.fn(); + public requestCapabilityToSendToDevice = vi.fn(); + public requestCapabilityToReceiveToDevice = vi.fn(); + public sendRoomEvent = vi.fn( (eventType: string, content: unknown, roomId?: string, delay?: number, parentDelayId?: string) => delay === undefined && parentDelayId === undefined ? { event_id: `$${Math.random()}` } : { delay_id: `id-${Math.random()}` }, ); - public sendStateEvent = jest.fn( + public sendStateEvent = vi.fn( ( eventType: string, stateKey: string, @@ -81,22 +82,22 @@ class MockWidgetApi extends EventEmitter { ? { event_id: `$${Math.random()}` } : { delay_id: `id-${Math.random()}` }, ); - public updateDelayedEvent = jest.fn(); - public sendToDevice = jest.fn(); - public requestOpenIDConnectToken = jest.fn(() => { + public updateDelayedEvent = vi.fn(); + public sendToDevice = vi.fn(); + public requestOpenIDConnectToken = vi.fn(() => { return testOIDCToken; return new Promise(() => { return testOIDCToken; }); }); - public readStateEvents = jest.fn(() => []); - public getTurnServers = jest.fn(() => []); - public sendContentLoaded = jest.fn(); + public readStateEvents = vi.fn(() => []); + public getTurnServers = vi.fn(() => []); + public sendContentLoaded = vi.fn(); public transport = { - reply: jest.fn(), - send: jest.fn(), - sendComplete: jest.fn(), + reply: vi.fn(), + send: vi.fn(), + sendComplete: vi.fn(), }; } @@ -115,11 +116,13 @@ declare module "../../src/types" { } describe("RoomWidgetClient", () => { - let widgetApi: MockedObject; + let widgetApi: MockedObject & { transport: MockedObject }; let client: MatrixClient; beforeEach(() => { - widgetApi = new MockWidgetApi() as unknown as MockedObject; + widgetApi = new MockWidgetApi() as unknown as MockedObject & { + transport: MockedObject; + }; }); afterEach(() => { @@ -213,7 +216,7 @@ describe("RoomWidgetClient", () => { ); expect(widgetApi.requestCapabilityForRoomTimeline).toHaveBeenCalledWith("!1:example.org"); expect(widgetApi.requestCapabilityToReceiveEvent).toHaveBeenCalledWith("org.matrix.rageshake_request"); - const injectSpy = jest.spyOn((client as any).syncApi, "injectRoomEvents"); + const injectSpy = vi.spyOn((client as any).syncApi, "injectRoomEvents"); const widgetSendEmitter = new EventEmitter(); const widgetSendPromise = new Promise((resolve) => widgetSendEmitter.once("send", () => resolve()), @@ -381,7 +384,7 @@ describe("RoomWidgetClient", () => { describe("delayed events", () => { describe("when supported", () => { - const doesServerSupportUnstableFeatureMock = jest.fn((feature) => + const doesServerSupportUnstableFeatureMock = vi.fn((feature) => Promise.resolve(feature === "org.matrix.msc4140"), ); diff --git a/spec/unit/event-mapper.spec.ts b/spec/unit/event-mapper.spec.ts index a2331316b69..e2d53195c1f 100644 --- a/spec/unit/event-mapper.spec.ts +++ b/spec/unit/event-mapper.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + import { MatrixClient, MatrixEvent, MatrixEventEvent, MatrixScheduler, Room } from "../../src"; import { eventMapperFor } from "../../src/event-mapper"; import { IStore } from "../../src/store"; @@ -37,7 +39,7 @@ describe("eventMapperFor", function () { setUserCreator(_) {}, } as IStore, scheduler: { - setProcessFunction: jest.fn(), + setProcessFunction: vi.fn(), } as unknown as MatrixScheduler, userId: userId, }); @@ -133,7 +135,7 @@ describe("eventMapperFor", function () { event_id: eventId, }; - const decryptEventIfNeededSpy = jest.spyOn(client, "decryptEventIfNeeded"); + const decryptEventIfNeededSpy = vi.spyOn(client, "decryptEventIfNeeded"); decryptEventIfNeededSpy.mockResolvedValue(); // stub it out const mapper = eventMapperFor(client, { @@ -161,7 +163,7 @@ describe("eventMapperFor", function () { event_id: eventId, }; - const evListener = jest.fn(); + const evListener = vi.fn(); client.on(MatrixEventEvent.Replaced, evListener); const noReEmitMapper = eventMapperFor(client, { diff --git a/spec/unit/event-timeline-set.spec.ts b/spec/unit/event-timeline-set.spec.ts index 352e882130c..ebdf2b2a0e0 100644 --- a/spec/unit/event-timeline-set.spec.ts +++ b/spec/unit/event-timeline-set.spec.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { mocked } from "jest-mock"; +import { beforeEach, describe, expect, it, MockInstance, vi } from "vitest"; import * as utils from "../test-utils/test-utils"; import { @@ -31,6 +31,7 @@ import { } from "../../src"; import { Thread } from "../../src/models/thread"; import { ReEmitter } from "../../src/ReEmitter"; +import { mocked } from "../test-utils"; describe("EventTimelineSet", () => { const roomId = "!foo:bar"; @@ -228,7 +229,7 @@ describe("EventTimelineSet", () => { let thread: Thread; beforeEach(() => { - (client.supportsThreads as jest.Mock).mockReturnValue(true); + mocked(client.supportsThreads).mockReturnValue(true); thread = new Thread("!thread_id:server", messageEvent, { room, client }); }); @@ -306,20 +307,20 @@ describe("EventTimelineSet", () => { }); describe("with events to be decrypted", () => { - let messageEventShouldAttemptDecryptionSpy: jest.SpyInstance; - let messageEventIsDecryptionFailureSpy: jest.SpyInstance; + let messageEventShouldAttemptDecryptionSpy: MockInstance; + let messageEventIsDecryptionFailureSpy: MockInstance; - let replyEventShouldAttemptDecryptionSpy: jest.SpyInstance; - let replyEventIsDecryptionFailureSpy: jest.SpyInstance; + let replyEventShouldAttemptDecryptionSpy: MockInstance; + let replyEventIsDecryptionFailureSpy: MockInstance; beforeEach(() => { - messageEventShouldAttemptDecryptionSpy = jest.spyOn(messageEvent, "shouldAttemptDecryption"); + messageEventShouldAttemptDecryptionSpy = vi.spyOn(messageEvent, "shouldAttemptDecryption"); messageEventShouldAttemptDecryptionSpy.mockReturnValue(true); - messageEventIsDecryptionFailureSpy = jest.spyOn(messageEvent, "isDecryptionFailure"); + messageEventIsDecryptionFailureSpy = vi.spyOn(messageEvent, "isDecryptionFailure"); - replyEventShouldAttemptDecryptionSpy = jest.spyOn(replyEvent, "shouldAttemptDecryption"); + replyEventShouldAttemptDecryptionSpy = vi.spyOn(replyEvent, "shouldAttemptDecryption"); replyEventShouldAttemptDecryptionSpy.mockReturnValue(true); - replyEventIsDecryptionFailureSpy = jest.spyOn(messageEvent, "isDecryptionFailure"); + replyEventIsDecryptionFailureSpy = vi.spyOn(messageEvent, "isDecryptionFailure"); eventTimelineSet.addEventsToTimeline([messageEvent, replyEvent], true, false, eventTimeline, "foo"); }); @@ -384,7 +385,7 @@ describe("EventTimelineSet", () => { let thread: Thread; beforeEach(() => { - (client.supportsThreads as jest.Mock).mockReturnValue(true); + mocked(client.supportsThreads).mockReturnValue(true); thread = new Thread("!thread_id:server", messageEvent, { room, client }); }); diff --git a/spec/unit/event-timeline.spec.ts b/spec/unit/event-timeline.spec.ts index fab355eca69..d5d34464141 100644 --- a/spec/unit/event-timeline.spec.ts +++ b/spec/unit/event-timeline.spec.ts @@ -1,4 +1,4 @@ -import { mocked } from "jest-mock"; +import { beforeEach, describe, expect, it, vi } from "vitest"; import * as utils from "../test-utils/test-utils"; import { Direction, EventTimeline } from "../../src/models/event-timeline"; @@ -8,6 +8,7 @@ import { Room } from "../../src/models/room"; import { RoomMember } from "../../src/models/room-member"; import { EventTimelineSet } from "../../src/models/event-timeline-set"; import { KnownMembership } from "../../src/@types/membership"; +import { mocked } from "../test-utils"; describe("EventTimeline", function () { const roomId = "!foo:bar"; @@ -20,21 +21,21 @@ describe("EventTimeline", function () { const getTimeline = (): EventTimeline => { const room = new Room(roomId, mockClient, userA); const timelineSet = new EventTimelineSet(room); - jest.spyOn(room, "getUnfilteredTimelineSet").mockReturnValue(timelineSet); + vi.spyOn(room, "getUnfilteredTimelineSet").mockReturnValue(timelineSet); const timeline = new EventTimeline(timelineSet); // We manually stub the methods we'll be mocking out later instead of mocking the whole module // otherwise the default member property values (e.g. paginationToken) will be incorrect - timeline.getState(Direction.Backward)!.setStateEvents = jest.fn(); - timeline.getState(Direction.Backward)!.getSentinelMember = jest.fn(); - timeline.getState(Direction.Forward)!.setStateEvents = jest.fn(); - timeline.getState(Direction.Forward)!.getSentinelMember = jest.fn(); + timeline.getState(Direction.Backward)!.setStateEvents = vi.fn(); + timeline.getState(Direction.Backward)!.getSentinelMember = vi.fn(); + timeline.getState(Direction.Forward)!.setStateEvents = vi.fn(); + timeline.getState(Direction.Forward)!.getSentinelMember = vi.fn(); return timeline; }; beforeEach(function () { // reset any RoomState mocks - jest.resetAllMocks(); + vi.resetAllMocks(); timeline = getTimeline(); }); @@ -67,12 +68,12 @@ describe("EventTimeline", function () { timeline.initialiseState(events); // @ts-ignore private prop const timelineStartState = timeline.startState!; - expect(mocked(timelineStartState).setStateEvents).toHaveBeenCalledWith(events, { + expect(mocked(timelineStartState.setStateEvents)).toHaveBeenCalledWith(events, { timelineWasEmpty: undefined, }); // @ts-ignore private prop const timelineEndState = timeline.endState!; - expect(mocked(timelineEndState).setStateEvents).toHaveBeenCalledWith(events, { + expect(mocked(timelineEndState.setStateEvents)).toHaveBeenCalledWith(events, { timelineWasEmpty: undefined, }); }); @@ -210,13 +211,13 @@ describe("EventTimeline", function () { sentinel.name = "Old Alice"; sentinel.membership = KnownMembership.Join; - mocked(timeline.getState(EventTimeline.FORWARDS)!).getSentinelMember.mockImplementation(function (uid) { + mocked(timeline.getState(EventTimeline.FORWARDS)!.getSentinelMember).mockImplementation(function (uid) { if (uid === userA) { return sentinel; } return null; }); - mocked(timeline.getState(EventTimeline.BACKWARDS)!).getSentinelMember.mockImplementation(function (uid) { + mocked(timeline.getState(EventTimeline.BACKWARDS)!.getSentinelMember).mockImplementation(function (uid) { if (uid === userA) { return oldSentinel; } @@ -253,13 +254,13 @@ describe("EventTimeline", function () { sentinel.name = "Old Alice"; sentinel.membership = KnownMembership.Join; - mocked(timeline.getState(EventTimeline.FORWARDS)!).getSentinelMember.mockImplementation(function (uid) { + mocked(timeline.getState(EventTimeline.FORWARDS)!.getSentinelMember).mockImplementation(function (uid) { if (uid === userA) { return sentinel; } return null; }); - mocked(timeline.getState(EventTimeline.BACKWARDS)!).getSentinelMember.mockImplementation(function (uid) { + mocked(timeline.getState(EventTimeline.BACKWARDS)!.getSentinelMember).mockImplementation(function (uid) { if (uid === userA) { return oldSentinel; } diff --git a/spec/unit/extensible_events_v1/ExtensibleEvent.spec.ts b/spec/unit/extensible_events_v1/ExtensibleEvent.spec.ts index 2a0839efa39..d8f884cbfa9 100644 --- a/spec/unit/extensible_events_v1/ExtensibleEvent.spec.ts +++ b/spec/unit/extensible_events_v1/ExtensibleEvent.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { describe, expect, it } from "vitest"; + import { ExtensibleEventType, IPartialEvent } from "../../../src/@types/extensible_events"; import { ExtensibleEvent } from "../../../src/extensible_events_v1/ExtensibleEvent"; diff --git a/spec/unit/extensible_events_v1/MessageEvent.spec.ts b/spec/unit/extensible_events_v1/MessageEvent.spec.ts index cb41f1de978..2fd0dbc3c60 100644 --- a/spec/unit/extensible_events_v1/MessageEvent.spec.ts +++ b/spec/unit/extensible_events_v1/MessageEvent.spec.ts @@ -14,13 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { - ExtensibleAnyMessageEventContent, - IPartialEvent, - M_HTML, - M_MESSAGE, - M_TEXT, -} from "../../../src/@types/extensible_events"; +import { describe, expect, it } from "vitest"; + +import { ExtensibleAnyMessageEventContent, IPartialEvent, M_HTML, M_MESSAGE, M_TEXT } from "../../../src/@types/extensible_events"; import { MessageEvent } from "../../../src/extensible_events_v1/MessageEvent"; import { InvalidEventError } from "../../../src/extensible_events_v1/InvalidEventError"; diff --git a/spec/unit/extensible_events_v1/PollEndEvent.spec.ts b/spec/unit/extensible_events_v1/PollEndEvent.spec.ts index 349e3fc58f4..03f5661cd05 100644 --- a/spec/unit/extensible_events_v1/PollEndEvent.spec.ts +++ b/spec/unit/extensible_events_v1/PollEndEvent.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { describe, expect, it } from "vitest"; + import { PollEndEventContent, M_POLL_END } from "../../../src/@types/polls"; import { IPartialEvent, REFERENCE_RELATION, M_TEXT } from "../../../src/@types/extensible_events"; import { PollEndEvent } from "../../../src/extensible_events_v1/PollEndEvent"; diff --git a/spec/unit/extensible_events_v1/PollResponseEvent.spec.ts b/spec/unit/extensible_events_v1/PollResponseEvent.spec.ts index 6abdd824e64..7952ea78c50 100644 --- a/spec/unit/extensible_events_v1/PollResponseEvent.spec.ts +++ b/spec/unit/extensible_events_v1/PollResponseEvent.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { describe, expect, it } from "vitest"; + import { M_TEXT, IPartialEvent, REFERENCE_RELATION } from "../../../src/@types/extensible_events"; import { M_POLL_START, diff --git a/spec/unit/extensible_events_v1/PollStartEvent.spec.ts b/spec/unit/extensible_events_v1/PollStartEvent.spec.ts index 93612069bf4..741b65e75a0 100644 --- a/spec/unit/extensible_events_v1/PollStartEvent.spec.ts +++ b/spec/unit/extensible_events_v1/PollStartEvent.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { describe, expect, it } from "vitest"; + import { M_TEXT, IPartialEvent } from "../../../src/@types/extensible_events"; import { M_POLL_START, diff --git a/spec/unit/extensible_events_v1/utilities.spec.ts b/spec/unit/extensible_events_v1/utilities.spec.ts index 9fd3636de4e..de2227e29ff 100644 --- a/spec/unit/extensible_events_v1/utilities.spec.ts +++ b/spec/unit/extensible_events_v1/utilities.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { describe, expect, it } from "vitest"; + import { NamespacedValue } from "matrix-events-sdk"; import { isEventTypeSame } from "../../../src/@types/extensible_events"; diff --git a/spec/unit/feature.spec.ts b/spec/unit/feature.spec.ts index 97420947d56..cbbdf11e0d6 100644 --- a/spec/unit/feature.spec.ts +++ b/spec/unit/feature.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { describe, expect, it } from "vitest"; + import { buildFeatureSupportMap, Feature, ServerSupport } from "../../src/feature"; describe("Feature detection", () => { diff --git a/spec/unit/filter-component.spec.ts b/spec/unit/filter-component.spec.ts index ad7cecde26a..78b6c9e563e 100644 --- a/spec/unit/filter-component.spec.ts +++ b/spec/unit/filter-component.spec.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from "vitest"; import { RelationType } from "../../src"; import { FilterComponent } from "../../src/filter-component"; import { mkEvent } from "../test-utils/test-utils"; diff --git a/spec/unit/filter.spec.ts b/spec/unit/filter.spec.ts index 6a31f691b62..aa66a6145fa 100644 --- a/spec/unit/filter.spec.ts +++ b/spec/unit/filter.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { beforeEach, describe, expect, it } from "vitest"; + import { UNREAD_THREAD_NOTIFICATIONS } from "../../src/@types/sync"; import { Filter, IFilterDefinition } from "../../src/filter"; import { mkEvent } from "../test-utils/test-utils"; diff --git a/spec/unit/http-api/__snapshots__/index.spec.ts.snap b/spec/unit/http-api/__snapshots__/index.spec.ts.snap index f60866aa238..d985ad6e663 100644 --- a/spec/unit/http-api/__snapshots__/index.spec.ts.snap +++ b/spec/unit/http-api/__snapshots__/index.spec.ts.snap @@ -1,4 +1,14 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`MatrixHttpApi > should return expected object from \`getContentUri\` 1`] = ` +{ + "base": "http://baseUrl", + "params": { + "access_token": "token", + }, + "path": "/_matrix/media/v3/upload", +} +`; exports[`MatrixHttpApi should return expected object from \`getContentUri\` 1`] = ` { diff --git a/spec/unit/http-api/errors.spec.ts b/spec/unit/http-api/errors.spec.ts index bcf3aa45547..bd05fb30eca 100644 --- a/spec/unit/http-api/errors.spec.ts +++ b/spec/unit/http-api/errors.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { beforeEach, describe, expect, it, vi } from "vitest"; + import { MatrixError } from "../../../src"; type IErrorJson = MatrixError["data"]; @@ -57,7 +59,7 @@ describe("MatrixError", () => { it("should retrieve Date Retry-After header from rate-limit error", () => { headers.set("Retry-After", `${new Date(160000).toUTCString()}`); - jest.spyOn(globalThis.Date, "now").mockImplementationOnce(() => 100000); + vi.spyOn(globalThis.Date, "now").mockImplementationOnce(() => 100000); const err = makeMatrixError(429, { errcode: "M_LIMIT_EXCEEDED", retry_after_ms: 150000 }); expect(err.isRateLimitError()).toBe(true); // prefer Retry-After header over retry_after_ms diff --git a/spec/unit/http-api/fetch.spec.ts b/spec/unit/http-api/fetch.spec.ts index 294ec8df004..85df320c61c 100644 --- a/spec/unit/http-api/fetch.spec.ts +++ b/spec/unit/http-api/fetch.spec.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { Mocked } from "jest-mock"; +import { describe, expect, it, vi, Mocked } from "vitest"; import { FetchHttpApi } from "../../../src/http-api/fetch"; import { TypedEventEmitter } from "../../../src/models/typed-event-emitter"; @@ -37,7 +37,7 @@ describe("FetchHttpApi", () => { const prefix = ClientPrefix.V3; it("should support aborting multiple times", () => { - const fetchFn = jest.fn().mockResolvedValue({ ok: true }); + const fetchFn = vi.fn().mockResolvedValue({ ok: true }); const api = new FetchHttpApi(new TypedEventEmitter(), { baseUrl, prefix, fetchFn }); api.request(Method.Get, "/foo"); @@ -60,7 +60,7 @@ describe("FetchHttpApi", () => { }); it("should fall back to global fetch if fetchFn not provided", () => { - globalThis.fetch = jest.fn(); + globalThis.fetch = vi.fn(); expect(globalThis.fetch).not.toHaveBeenCalled(); const api = new FetchHttpApi(new TypedEventEmitter(), { baseUrl, prefix }); api.fetch("test"); @@ -83,7 +83,7 @@ describe("FetchHttpApi", () => { }); it("should send params as query string for GET requests", () => { - const fetchFn = jest.fn().mockResolvedValue({ ok: true }); + const fetchFn = vi.fn().mockResolvedValue({ ok: true }); const api = new FetchHttpApi(new TypedEventEmitter(), { baseUrl, idBaseUrl, prefix, fetchFn }); api.idServerRequest(Method.Get, "/test", { foo: "bar", via: ["a", "b"] }, IdentityPrefix.V2); expect(fetchFn.mock.calls[0][0].searchParams.get("foo")).toBe("bar"); @@ -91,7 +91,7 @@ describe("FetchHttpApi", () => { }); it("should send params as body for non-GET requests", () => { - const fetchFn = jest.fn().mockResolvedValue({ ok: true }); + const fetchFn = vi.fn().mockResolvedValue({ ok: true }); const api = new FetchHttpApi(new TypedEventEmitter(), { baseUrl, idBaseUrl, prefix, fetchFn }); const params = { foo: "bar", via: ["a", "b"] }; api.idServerRequest(Method.Post, "/test", params, IdentityPrefix.V2); @@ -100,7 +100,7 @@ describe("FetchHttpApi", () => { }); it("should add Authorization header if token provided", () => { - const fetchFn = jest.fn().mockResolvedValue({ ok: true }); + const fetchFn = vi.fn().mockResolvedValue({ ok: true }); const api = new FetchHttpApi(new TypedEventEmitter(), { baseUrl, idBaseUrl, prefix, fetchFn }); api.idServerRequest(Method.Post, "/test", {}, IdentityPrefix.V2, "token"); expect(fetchFn.mock.calls[0][1].headers.Authorization).toBe("Bearer token"); @@ -109,14 +109,14 @@ describe("FetchHttpApi", () => { it("should return the Response object if onlyData=false", async () => { const res = { ok: true }; - const fetchFn = jest.fn().mockResolvedValue(res); + const fetchFn = vi.fn().mockResolvedValue(res); const api = new FetchHttpApi(new TypedEventEmitter(), { baseUrl, prefix, fetchFn, onlyData: false }); await expect(api.requestOtherUrl(Method.Get, "http://url")).resolves.toBe(res); }); it("should return text if json=false", async () => { const text = "418 I'm a teapot"; - const fetchFn = jest.fn().mockResolvedValue({ ok: true, text: jest.fn().mockResolvedValue(text) }); + const fetchFn = vi.fn().mockResolvedValue({ ok: true, text: vi.fn().mockResolvedValue(text) }); const api = new FetchHttpApi(new TypedEventEmitter(), { baseUrl, prefix, fetchFn, onlyData: true }); await expect( api.requestOtherUrl(Method.Get, "http://url", undefined, { @@ -126,7 +126,7 @@ describe("FetchHttpApi", () => { }); it("should send token via query params if useAuthorizationHeader=false", () => { - const fetchFn = jest.fn().mockResolvedValue({ ok: true }); + const fetchFn = vi.fn().mockResolvedValue({ ok: true }); const api = new FetchHttpApi(new TypedEventEmitter(), { baseUrl, prefix, @@ -139,7 +139,7 @@ describe("FetchHttpApi", () => { }); it("should send token via headers by default", () => { - const fetchFn = jest.fn().mockResolvedValue({ ok: true }); + const fetchFn = vi.fn().mockResolvedValue({ ok: true }); const api = new FetchHttpApi(new TypedEventEmitter(), { baseUrl, prefix, @@ -151,7 +151,7 @@ describe("FetchHttpApi", () => { }); it("should not send a token if not calling `authedRequest`", () => { - const fetchFn = jest.fn().mockResolvedValue({ ok: true }); + const fetchFn = vi.fn().mockResolvedValue({ ok: true }); const api = new FetchHttpApi(new TypedEventEmitter(), { baseUrl, prefix, @@ -164,7 +164,7 @@ describe("FetchHttpApi", () => { }); it("should ensure no token is leaked out via query params if sending via headers", () => { - const fetchFn = jest.fn().mockResolvedValue({ ok: true }); + const fetchFn = vi.fn().mockResolvedValue({ ok: true }); const api = new FetchHttpApi(new TypedEventEmitter(), { baseUrl, prefix, @@ -178,7 +178,7 @@ describe("FetchHttpApi", () => { }); it("should not override manually specified access token via query params", () => { - const fetchFn = jest.fn().mockResolvedValue({ ok: true }); + const fetchFn = vi.fn().mockResolvedValue({ ok: true }); const api = new FetchHttpApi(new TypedEventEmitter(), { baseUrl, prefix, @@ -191,7 +191,7 @@ describe("FetchHttpApi", () => { }); it("should not override manually specified access token via header", () => { - const fetchFn = jest.fn().mockResolvedValue({ ok: true }); + const fetchFn = vi.fn().mockResolvedValue({ ok: true }); const api = new FetchHttpApi(new TypedEventEmitter(), { baseUrl, prefix, @@ -206,7 +206,7 @@ describe("FetchHttpApi", () => { }); it("should not override Accept header", () => { - const fetchFn = jest.fn().mockResolvedValue({ ok: true }); + const fetchFn = vi.fn().mockResolvedValue({ ok: true }); const api = new FetchHttpApi(new TypedEventEmitter(), { baseUrl, prefix, fetchFn }); api.authedRequest(Method.Get, "/path", undefined, undefined, { headers: { Accept: "text/html" }, @@ -215,14 +215,14 @@ describe("FetchHttpApi", () => { }); it("should emit NoConsent when given errcode=M_CONTENT_NOT_GIVEN", async () => { - const fetchFn = jest.fn().mockResolvedValue({ + const fetchFn = vi.fn().mockResolvedValue({ ok: false, headers: { get(name: string): string | null { return name === "Content-Type" ? "application/json" : null; }, }, - text: jest.fn().mockResolvedValue( + text: vi.fn().mockResolvedValue( JSON.stringify({ errcode: "M_CONSENT_NOT_GIVEN", error: "Ye shall ask for consent", @@ -240,7 +240,7 @@ describe("FetchHttpApi", () => { describe("authedRequest", () => { it("should not include token if unset", async () => { - const fetchFn = jest.fn().mockResolvedValue({ ok: true }); + const fetchFn = vi.fn().mockResolvedValue({ ok: true }); const emitter = new TypedEventEmitter(); const api = new FetchHttpApi(emitter, { baseUrl, prefix, fetchFn }); await api.authedRequest(Method.Post, "/account/password"); @@ -266,7 +266,7 @@ describe("FetchHttpApi", () => { return name === "Content-Type" ? "application/json" : null; }, }, - text: jest.fn().mockResolvedValue(JSON.stringify(unknownTokenErrBody)), + text: vi.fn().mockResolvedValue(JSON.stringify(unknownTokenErrBody)), }; const okayResponse = { ok: true, @@ -275,9 +275,9 @@ describe("FetchHttpApi", () => { describe("without a tokenRefreshFunction", () => { it("should emit logout and throw", async () => { - const fetchFn = jest.fn().mockResolvedValue(unknownTokenResponse); + const fetchFn = vi.fn().mockResolvedValue(unknownTokenResponse); const emitter = new TypedEventEmitter(); - jest.spyOn(emitter, "emit"); + vi.spyOn(emitter, "emit"); const api = new FetchHttpApi(emitter, { baseUrl, prefix, fetchFn, accessToken, refreshToken }); await expect(api.authedRequest(Method.Post, "/account/password")).rejects.toThrow( unknownTokenErr, @@ -289,10 +289,10 @@ describe("FetchHttpApi", () => { describe("with a tokenRefreshFunction", () => { it("should emit logout and throw when token refresh fails", async () => { const error = new Error("uh oh"); - const tokenRefreshFunction = jest.fn().mockRejectedValue(error); - const fetchFn = jest.fn().mockResolvedValue(unknownTokenResponse); + const tokenRefreshFunction = vi.fn().mockRejectedValue(error); + const fetchFn = vi.fn().mockResolvedValue(unknownTokenResponse); const emitter = new TypedEventEmitter(); - jest.spyOn(emitter, "emit"); + vi.spyOn(emitter, "emit"); const api = new FetchHttpApi(emitter, { baseUrl, prefix, @@ -311,16 +311,16 @@ describe("FetchHttpApi", () => { it("should refresh token and retry request", async () => { const newAccessToken = "new-access-token"; const newRefreshToken = "new-refresh-token"; - const tokenRefreshFunction = jest.fn().mockResolvedValue({ + const tokenRefreshFunction = vi.fn().mockResolvedValue({ accessToken: newAccessToken, refreshToken: newRefreshToken, }); - const fetchFn = jest + const fetchFn = vi .fn() .mockResolvedValueOnce(unknownTokenResponse) .mockResolvedValueOnce(okayResponse); const emitter = new TypedEventEmitter(); - jest.spyOn(emitter, "emit"); + vi.spyOn(emitter, "emit"); const api = new FetchHttpApi(emitter, { baseUrl, prefix, @@ -342,16 +342,16 @@ describe("FetchHttpApi", () => { it("should only try to refresh the token once", async () => { const newAccessToken = "new-access-token"; const newRefreshToken = "new-refresh-token"; - const tokenRefreshFunction = jest.fn().mockResolvedValue({ + const tokenRefreshFunction = vi.fn().mockResolvedValue({ accessToken: newAccessToken, refreshToken: newRefreshToken, }); // fetch doesn't like our new or old tokens - const fetchFn = jest.fn().mockResolvedValue(unknownTokenResponse); + const fetchFn = vi.fn().mockResolvedValue(unknownTokenResponse); const emitter = new TypedEventEmitter(); - jest.spyOn(emitter, "emit"); + vi.spyOn(emitter, "emit"); const api = new FetchHttpApi(emitter, { baseUrl, prefix, @@ -384,7 +384,7 @@ describe("FetchHttpApi", () => { const localBaseUrl = "http://baseurl"; const baseUrlWithTrailingSlash = "http://baseurl/"; const makeApi = (thisBaseUrl = baseUrl): FetchHttpApi => { - const fetchFn = jest.fn(); + const fetchFn = vi.fn(); const emitter = new TypedEventEmitter(); return new FetchHttpApi(emitter, { baseUrl: thisBaseUrl, prefix, fetchFn }); }; @@ -437,11 +437,11 @@ describe("FetchHttpApi", () => { }); it("should not log query parameters", async () => { - jest.useFakeTimers(); + vi.useFakeTimers(); const deferred = defer(); - const fetchFn = jest.fn().mockReturnValue(deferred.promise); + const fetchFn = vi.fn().mockReturnValue(deferred.promise); const mockLogger = { - debug: jest.fn(), + debug: vi.fn(), } as unknown as Mocked; const api = new FetchHttpApi(new TypedEventEmitter(), { baseUrl, @@ -450,7 +450,7 @@ describe("FetchHttpApi", () => { logger: mockLogger, }); const prom = api.requestOtherUrl(Method.Get, "https://server:8448/some/path?query=param#fragment"); - jest.advanceTimersByTime(1234); + vi.advanceTimersByTime(1234); deferred.resolve({ ok: true, status: 200, text: () => Promise.resolve("RESPONSE") } as Response); await prom; expect(mockLogger.debug).not.toHaveBeenCalledWith("fragment"); diff --git a/spec/unit/http-api/index.spec.ts b/spec/unit/http-api/index.spec.ts index 5435c4bdcae..49ffed0f6d3 100644 --- a/spec/unit/http-api/index.spec.ts +++ b/spec/unit/http-api/index.spec.ts @@ -14,14 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { mocked } from "jest-mock"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { ClientPrefix, MatrixHttpApi, Method, UploadResponse } from "../../../src"; import { TypedEventEmitter } from "../../../src/models/typed-event-emitter"; +import { mocked } from "../../test-utils"; type Writeable = { -readonly [P in keyof T]: T[P] }; -jest.useFakeTimers(); +vi.useFakeTimers(); describe("MatrixHttpApi", () => { const baseUrl = "http://baseUrl"; @@ -35,17 +36,17 @@ describe("MatrixHttpApi", () => { beforeEach(() => { xhr = { upload: {} as XMLHttpRequestUpload, - open: jest.fn(), - send: jest.fn(), - abort: jest.fn(), - setRequestHeader: jest.fn(), + open: vi.fn(), + send: vi.fn(), + abort: vi.fn(), + setRequestHeader: vi.fn(), onreadystatechange: undefined, - getResponseHeader: jest.fn(), - getAllResponseHeaders: jest.fn(), + getResponseHeader: vi.fn(), + getAllResponseHeaders: vi.fn(), } as unknown as XMLHttpRequest; // We stub out XHR here as it is not available in JSDOM // @ts-ignore - globalThis.XMLHttpRequest = jest.fn().mockReturnValue(xhr); + globalThis.XMLHttpRequest = vi.fn().mockReturnValue(xhr); // @ts-ignore globalThis.XMLHttpRequest.DONE = DONE; }); @@ -61,14 +62,14 @@ describe("MatrixHttpApi", () => { it("should fall back to `fetch` where xhr is unavailable", () => { globalThis.XMLHttpRequest = undefined!; - const fetchFn = jest.fn().mockResolvedValue({ ok: true, json: jest.fn().mockResolvedValue({}) }); + const fetchFn = vi.fn().mockResolvedValue({ ok: true, json: vi.fn().mockResolvedValue({}) }); const api = new MatrixHttpApi(new TypedEventEmitter(), { baseUrl, prefix, fetchFn }); upload = api.uploadContent({} as File); expect(fetchFn).toHaveBeenCalled(); }); it("should prefer xhr where available", () => { - const fetchFn = jest.fn().mockResolvedValue({ ok: true }); + const fetchFn = vi.fn().mockResolvedValue({ ok: true }); const api = new MatrixHttpApi(new TypedEventEmitter(), { baseUrl, prefix, fetchFn }); upload = api.uploadContent({} as File); expect(fetchFn).not.toHaveBeenCalled(); @@ -127,18 +128,18 @@ describe("MatrixHttpApi", () => { it("should timeout if no progress in 30s", () => { const api = new MatrixHttpApi(new TypedEventEmitter(), { baseUrl, prefix }); upload = api.uploadContent({} as File); - jest.advanceTimersByTime(25000); + vi.advanceTimersByTime(25000); // @ts-ignore xhr.upload.onprogress(new Event("progress", { loaded: 1, total: 100 })); - jest.advanceTimersByTime(25000); + vi.advanceTimersByTime(25000); expect(xhr.abort).not.toHaveBeenCalled(); - jest.advanceTimersByTime(5000); + vi.advanceTimersByTime(5000); expect(xhr.abort).toHaveBeenCalled(); }); it("should call progressHandler", () => { const api = new MatrixHttpApi(new TypedEventEmitter(), { baseUrl, prefix }); - const progressHandler = jest.fn(); + const progressHandler = vi.fn(); upload = api.uploadContent({} as File, { progressHandler }); const progressEvent = new Event("progress") as ProgressEvent; Object.assign(progressEvent, { loaded: 1, total: 100 }); diff --git a/spec/unit/http-api/utils.spec.ts b/spec/unit/http-api/utils.spec.ts index 92c1e8ac20e..2612373303e 100644 --- a/spec/unit/http-api/utils.spec.ts +++ b/spec/unit/http-api/utils.spec.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { mocked } from "jest-mock"; +import { beforeEach, describe, expect, it, vi } from "vitest"; import { anySignal, @@ -26,39 +26,40 @@ import { timeoutSignal, } from "../../../src"; import { sleep } from "../../../src/utils"; +import { mocked } from "../../test-utils"; -jest.mock("../../../src/utils"); +vi.mock("../../../src/utils"); // setupTests mocks `timeoutSignal` due to hanging timers -jest.unmock("../../../src/http-api/utils"); +vi.unmock("../../../src/http-api/utils"); describe("timeoutSignal", () => { - jest.useFakeTimers(); + vi.useFakeTimers(); it("should fire abort signal after specified timeout", () => { const signal = timeoutSignal(3000); - const onabort = jest.fn(); + const onabort = vi.fn(); signal.onabort = onabort; expect(signal.aborted).toBeFalsy(); expect(onabort).not.toHaveBeenCalled(); - jest.advanceTimersByTime(3000); + vi.advanceTimersByTime(3000); expect(signal.aborted).toBeTruthy(); expect(onabort).toHaveBeenCalled(); }); }); describe("anySignal", () => { - jest.useFakeTimers(); + vi.useFakeTimers(); it("should fire when any signal fires", () => { const { signal } = anySignal([timeoutSignal(3000), timeoutSignal(2000)]); - const onabort = jest.fn(); + const onabort = vi.fn(); signal.onabort = onabort; expect(signal.aborted).toBeFalsy(); expect(onabort).not.toHaveBeenCalled(); - jest.advanceTimersByTime(2000); + vi.advanceTimersByTime(2000); expect(signal.aborted).toBeTruthy(); expect(onabort).toHaveBeenCalled(); }); @@ -66,13 +67,13 @@ describe("anySignal", () => { it("should cleanup when instructed", () => { const { signal, cleanup } = anySignal([timeoutSignal(3000), timeoutSignal(2000)]); - const onabort = jest.fn(); + const onabort = vi.fn(); signal.onabort = onabort; expect(signal.aborted).toBeFalsy(); expect(onabort).not.toHaveBeenCalled(); cleanup(); - jest.advanceTimersByTime(2000); + vi.advanceTimersByTime(2000); expect(signal.aborted).toBeFalsy(); expect(onabort).not.toHaveBeenCalled(); }); @@ -311,7 +312,7 @@ describe("parseErrorResponse", () => { describe("retryNetworkOperation", () => { it("should retry given number of times with exponential sleeps", async () => { const err = new ConnectionError("test"); - const fn = jest.fn().mockRejectedValue(err); + const fn = vi.fn().mockRejectedValue(err); mocked(sleep).mockResolvedValue(undefined); await expect(retryNetworkOperation(4, fn)).rejects.toThrow(err); expect(fn).toHaveBeenCalledTimes(4); @@ -323,7 +324,7 @@ describe("retryNetworkOperation", () => { it("should bail out on errors other than ConnectionError", async () => { const err = new TypeError("invalid JSON"); - const fn = jest.fn().mockRejectedValue(err); + const fn = vi.fn().mockRejectedValue(err); mocked(sleep).mockResolvedValue(undefined); await expect(retryNetworkOperation(3, fn)).rejects.toThrow(err); expect(fn).toHaveBeenCalledTimes(1); @@ -334,7 +335,7 @@ describe("retryNetworkOperation", () => { const err2 = new ConnectionError("test2"); const err3 = new ConnectionError("test3"); const errors = [err1, err2, err3]; - const fn = jest.fn().mockImplementation(() => { + const fn = vi.fn().mockImplementation(() => { throw errors.shift(); }); mocked(sleep).mockResolvedValue(undefined); diff --git a/spec/unit/interactive-auth.spec.ts b/spec/unit/interactive-auth.spec.ts index e867788ff35..c0fdbdca89f 100644 --- a/spec/unit/interactive-auth.spec.ts +++ b/spec/unit/interactive-auth.spec.ts @@ -15,6 +15,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { describe, expect, it, vi } from "vitest"; + import { MatrixClient } from "../../src/client"; import { logger } from "../../src/logger"; import { InteractiveAuth, AuthType } from "../../src/interactive-auth"; @@ -34,14 +36,14 @@ const getFakeClient = (): MatrixClient => new FakeClient() as unknown as MatrixC describe("InteractiveAuth", () => { it("should start an auth stage and complete it", async () => { - const doRequest = jest.fn(); - const stateUpdated = jest.fn(); + const doRequest = vi.fn(); + const stateUpdated = vi.fn(); const ia = new InteractiveAuth({ matrixClient: getFakeClient(), doRequest: doRequest, stateUpdated: stateUpdated, - requestEmailToken: jest.fn(), + requestEmailToken: vi.fn(), authData: { session: "sessionId", flows: [{ stages: [AuthType.Password] }], @@ -83,14 +85,14 @@ describe("InteractiveAuth", () => { }); it("should handle auth errcode presence", async () => { - const doRequest = jest.fn(); - const stateUpdated = jest.fn(); + const doRequest = vi.fn(); + const stateUpdated = vi.fn(); const ia = new InteractiveAuth({ matrixClient: getFakeClient(), doRequest: doRequest, stateUpdated: stateUpdated, - requestEmailToken: jest.fn(), + requestEmailToken: vi.fn(), authData: { session: "sessionId", flows: [{ stages: [AuthType.Password] }], @@ -132,9 +134,9 @@ describe("InteractiveAuth", () => { }); it("should handle set emailSid for email flow", async () => { - const doRequest = jest.fn(); - const stateUpdated = jest.fn(); - const requestEmailToken = jest.fn(); + const doRequest = vi.fn(); + const stateUpdated = vi.fn(); + const requestEmailToken = vi.fn(); const ia = new InteractiveAuth({ doRequest, @@ -186,9 +188,9 @@ describe("InteractiveAuth", () => { }); it("should make a request if no authdata is provided", async () => { - const doRequest = jest.fn(); - const stateUpdated = jest.fn(); - const requestEmailToken = jest.fn(); + const doRequest = vi.fn(); + const stateUpdated = vi.fn(); + const requestEmailToken = vi.fn(); const ia = new InteractiveAuth({ matrixClient: getFakeClient(), @@ -248,9 +250,9 @@ describe("InteractiveAuth", () => { }); it("should make a request if authdata is null", async () => { - const doRequest = jest.fn(); - const stateUpdated = jest.fn(); - const requestEmailToken = jest.fn(); + const doRequest = vi.fn(); + const stateUpdated = vi.fn(); + const requestEmailToken = vi.fn(); const ia = new InteractiveAuth({ matrixClient: getFakeClient(), @@ -310,9 +312,9 @@ describe("InteractiveAuth", () => { }); it("should start an auth stage and reject if no auth flow", async () => { - const doRequest = jest.fn(); - const stateUpdated = jest.fn(); - const requestEmailToken = jest.fn(); + const doRequest = vi.fn(); + const stateUpdated = vi.fn(); + const requestEmailToken = vi.fn(); const ia = new InteractiveAuth({ matrixClient: getFakeClient(), @@ -341,9 +343,9 @@ describe("InteractiveAuth", () => { }); it("should start an auth stage and reject if no auth flow but has session", async () => { - const doRequest = jest.fn(); - const stateUpdated = jest.fn(); - const requestEmailToken = jest.fn(); + const doRequest = vi.fn(); + const stateUpdated = vi.fn(); + const requestEmailToken = vi.fn(); const ia = new InteractiveAuth({ matrixClient: getFakeClient(), @@ -376,9 +378,9 @@ describe("InteractiveAuth", () => { }); it("should handle unexpected error types without data property set", async () => { - const doRequest = jest.fn(); - const stateUpdated = jest.fn(); - const requestEmailToken = jest.fn(); + const doRequest = vi.fn(); + const stateUpdated = vi.fn(); + const requestEmailToken = vi.fn(); const ia = new InteractiveAuth({ matrixClient: getFakeClient(), @@ -401,9 +403,9 @@ describe("InteractiveAuth", () => { }); it("should allow dummy auth", async () => { - const doRequest = jest.fn(); - const stateUpdated = jest.fn(); - const requestEmailToken = jest.fn(); + const doRequest = vi.fn(); + const stateUpdated = vi.fn(); + const requestEmailToken = vi.fn(); const ia = new InteractiveAuth({ matrixClient: getFakeClient(), @@ -435,9 +437,9 @@ describe("InteractiveAuth", () => { describe("requestEmailToken", () => { it("increases auth attempts", async () => { - const doRequest = jest.fn(); - const stateUpdated = jest.fn(); - const requestEmailToken = jest.fn(); + const doRequest = vi.fn(); + const stateUpdated = vi.fn(); + const requestEmailToken = vi.fn(); requestEmailToken.mockImplementation(async () => ({ sid: "" })); const ia = new InteractiveAuth({ @@ -464,9 +466,9 @@ describe("InteractiveAuth", () => { }); it("passes errors through", async () => { - const doRequest = jest.fn(); - const stateUpdated = jest.fn(); - const requestEmailToken = jest.fn(); + const doRequest = vi.fn(); + const stateUpdated = vi.fn(); + const requestEmailToken = vi.fn(); requestEmailToken.mockImplementation(async () => { throw new Error("unspecific network error"); }); @@ -482,9 +484,9 @@ describe("InteractiveAuth", () => { }); it("only starts one request at a time", async () => { - const doRequest = jest.fn(); - const stateUpdated = jest.fn(); - const requestEmailToken = jest.fn(); + const doRequest = vi.fn(); + const stateUpdated = vi.fn(); + const requestEmailToken = vi.fn(); requestEmailToken.mockImplementation(() => sleep(500, { sid: "" })); const ia = new InteractiveAuth({ @@ -499,9 +501,9 @@ describe("InteractiveAuth", () => { }); it("stores result in email sid", async () => { - const doRequest = jest.fn(); - const stateUpdated = jest.fn(); - const requestEmailToken = jest.fn(); + const doRequest = vi.fn(); + const stateUpdated = vi.fn(); + const requestEmailToken = vi.fn(); const sid = secureRandomString(24); requestEmailToken.mockImplementation(() => sleep(500, { sid })); @@ -518,14 +520,14 @@ describe("InteractiveAuth", () => { }); it("should prioritise shorter flows", async () => { - const doRequest = jest.fn(); - const stateUpdated = jest.fn(); + const doRequest = vi.fn(); + const stateUpdated = vi.fn(); const ia = new InteractiveAuth({ matrixClient: getFakeClient(), doRequest: doRequest, stateUpdated: stateUpdated, - requestEmailToken: jest.fn(), + requestEmailToken: vi.fn(), authData: { session: "sessionId", flows: [{ stages: [AuthType.Recaptcha, AuthType.Password] }, { stages: [AuthType.Password] }], @@ -539,14 +541,14 @@ describe("InteractiveAuth", () => { }); it("should prioritise flows with entirely supported stages", async () => { - const doRequest = jest.fn(); - const stateUpdated = jest.fn(); + const doRequest = vi.fn(); + const stateUpdated = vi.fn(); const ia = new InteractiveAuth({ matrixClient: getFakeClient(), doRequest: doRequest, stateUpdated: stateUpdated, - requestEmailToken: jest.fn(), + requestEmailToken: vi.fn(), authData: { session: "sessionId", flows: [{ stages: ["com.devture.shared_secret_auth"] }, { stages: [AuthType.Password] }], @@ -561,14 +563,14 @@ describe("InteractiveAuth", () => { }); it("should fire stateUpdated callback with error when a request fails", async () => { - const doRequest = jest.fn(); - const stateUpdated = jest.fn(); + const doRequest = vi.fn(); + const stateUpdated = vi.fn(); const ia = new InteractiveAuth({ matrixClient: getFakeClient(), doRequest: doRequest, stateUpdated: stateUpdated, - requestEmailToken: jest.fn(), + requestEmailToken: vi.fn(), authData: { session: "sessionId", flows: [{ stages: [AuthType.Password] }], diff --git a/spec/unit/local_notifications.spec.ts b/spec/unit/local_notifications.spec.ts index e46d462ad96..8df9f8122a0 100644 --- a/spec/unit/local_notifications.spec.ts +++ b/spec/unit/local_notifications.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { beforeEach, describe, expect, it, vi } from "vitest"; + import { LocalNotificationSettings } from "../../src/@types/local_notifications"; import { LOCAL_NOTIFICATION_SETTINGS_PREFIX, MatrixClient } from "../../src/matrix"; import { TestClient } from "../TestClient"; @@ -23,7 +25,7 @@ let client: MatrixClient; describe("Local notification settings", () => { beforeEach(() => { client = new TestClient("@alice:matrix.org", "123", undefined, undefined, undefined).client; - client.setAccountData = jest.fn(); + client.setAccountData = vi.fn(); }); describe("Lets you set local notification settings", () => { diff --git a/spec/unit/location.spec.ts b/spec/unit/location.spec.ts index ff24d663801..dca63e1f3b7 100644 --- a/spec/unit/location.spec.ts +++ b/spec/unit/location.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { describe, expect, it } from "vitest"; + import { makeLocationContent, parseLocationEvent } from "../../src/content-helpers"; import { M_ASSET, diff --git a/spec/unit/login.spec.ts b/spec/unit/login.spec.ts index ff6f298a076..95c789fc03a 100644 --- a/spec/unit/login.spec.ts +++ b/spec/unit/login.spec.ts @@ -1,4 +1,5 @@ -import fetchMock from "fetch-mock-jest"; +import { afterEach, beforeEach, describe, expect, it } from "vitest"; +import fetchMock from "@fetch-mock/vitest"; import { ClientPrefix, MatrixClient } from "../../src"; import { SSOAction } from "../../src/@types/auth"; diff --git a/spec/unit/matrix-client.spec.ts b/spec/unit/matrix-client.spec.ts index fa261c9ce69..f10fe9b52e1 100644 --- a/spec/unit/matrix-client.spec.ts +++ b/spec/unit/matrix-client.spec.ts @@ -14,8 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { Mocked, mocked } from "jest-mock"; -import fetchMock from "fetch-mock-jest"; +import { it, describe, beforeEach, afterEach, expect, Mocked, vi, beforeAll, afterAll } from "vitest"; +import fetchMock from "@fetch-mock/vitest"; import { logger } from "../../src/logger"; import { ClientEvent, IMatrixClientCreateOpts, ITurnServerResponse, MatrixClient, Store } from "../../src/client"; @@ -40,6 +40,7 @@ import * as testUtils from "../test-utils/test-utils"; import { makeBeaconInfoContent } from "../../src/content-helpers"; import { M_BEACON_INFO } from "../../src/@types/beacon"; import { + Body, ClientPrefix, ConditionKind, ContentHelpers, @@ -78,12 +79,13 @@ import { CryptoBackend } from "../../src/common-crypto/CryptoBackend"; import { KnownMembership } from "../../src/@types/membership"; import { RoomMessageEventContent } from "../../src/@types/events"; import { mockOpenIdConfiguration } from "../test-utils/oidc.ts"; +import { mocked } from "../test-utils"; -jest.useFakeTimers(); +vi.useFakeTimers(); -jest.mock("../../src/webrtc/call", () => ({ - ...jest.requireActual("../../src/webrtc/call"), - supportsMatrixCall: jest.fn(() => false), +vi.mock("../../src/webrtc/call", async () => ({ + ...(await vi.importActual("../../src/webrtc/call")), + supportsMatrixCall: vi.fn(() => false), })); // Utility function to ease the transition from our QueryDict type to a Map @@ -208,7 +210,7 @@ describe("MatrixClient", function () { method: Method, path: string, queryParams?: QueryDict, - body?: BodyInit, + body?: Body, requestOpts: IRequestOpts = {}, ) { const { prefix } = requestOpts; @@ -314,7 +316,7 @@ describe("MatrixClient", function () { client.http = ( ["authedRequest", "getContentUri", "request", "uploadContent", "idServerRequest"] as const ).reduce((r, k) => { - r[k] = jest.fn(); + r[k] = vi.fn(); return r; }, {} as MatrixHttpApi); mocked(client.http.authedRequest).mockImplementation(httpReq); @@ -324,7 +326,7 @@ describe("MatrixClient", function () { beforeEach(function () { scheduler = (["getQueueForEvent", "queueEvent", "removeEventFromQueue", "setProcessFunction"] as const).reduce( (r, k) => { - r[k] = jest.fn(); + r[k] = vi.fn(); return r; }, {} as MatrixScheduler, @@ -351,15 +353,15 @@ describe("MatrixClient", function () { "setUserCreator", ] as const ).reduce((r, k) => { - r[k] = jest.fn(); + r[k] = vi.fn(); return r; }, {} as Store); - store.getSavedSync = jest.fn().mockReturnValue(Promise.resolve(null)); - store.getSavedSyncToken = jest.fn().mockReturnValue(Promise.resolve(null)); - store.setSyncData = jest.fn().mockReturnValue(Promise.resolve(null)); - store.getClientOptions = jest.fn().mockReturnValue(Promise.resolve(null)); - store.storeClientOptions = jest.fn().mockReturnValue(Promise.resolve(null)); - store.isNewlyCreated = jest.fn().mockReturnValue(Promise.resolve(true)); + store.getSavedSync = vi.fn().mockReturnValue(Promise.resolve(null)); + store.getSavedSyncToken = vi.fn().mockReturnValue(Promise.resolve(null)); + store.setSyncData = vi.fn().mockReturnValue(Promise.resolve(null)); + store.getClientOptions = vi.fn().mockReturnValue(Promise.resolve(null)); + store.storeClientOptions = vi.fn().mockReturnValue(Promise.resolve(null)); + store.isNewlyCreated = vi.fn().mockReturnValue(Promise.resolve(true)); // set unstableFeatures to a defined state before each test unstableFeatures = { @@ -1164,7 +1166,7 @@ describe("MatrixClient", function () { const roomId = "!room:example.org"; const roomName = "Test Tree"; const mockRoom = {} as unknown as Room; - const fn = jest.fn().mockImplementation((opts) => { + const fn = vi.fn().mockImplementation((opts) => { expect(opts).toMatchObject({ name: roomName, preset: Preset.PrivateChat, @@ -1216,7 +1218,6 @@ describe("MatrixClient", function () { getMyMembership: () => KnownMembership.Join, currentState: { getStateEvents: (eventType, stateKey) => { - /* eslint-disable jest/no-conditional-expect */ if (eventType === EventType.RoomCreate) { expect(stateKey).toEqual(""); return new MatrixEvent({ @@ -1235,7 +1236,6 @@ describe("MatrixClient", function () { } else { throw new Error("Unexpected event type or state key"); } - /* eslint-enable jest/no-conditional-expect */ }, } as Room["currentState"], } as unknown as Room; @@ -1278,7 +1278,6 @@ describe("MatrixClient", function () { getMyMembership: () => KnownMembership.Join, currentState: { getStateEvents: (eventType, stateKey) => { - /* eslint-disable jest/no-conditional-expect */ if (eventType === EventType.RoomCreate) { expect(stateKey).toEqual(""); return new MatrixEvent({ @@ -1297,7 +1296,6 @@ describe("MatrixClient", function () { } else { throw new Error("Unexpected event type or state key"); } - /* eslint-enable jest/no-conditional-expect */ }, } as Room["currentState"], } as unknown as Room; @@ -1315,7 +1313,6 @@ describe("MatrixClient", function () { getMyMembership: () => KnownMembership.Join, currentState: { getStateEvents: (eventType, stateKey) => { - /* eslint-disable jest/no-conditional-expect */ if (eventType === EventType.RoomCreate) { expect(stateKey).toEqual(""); return new MatrixEvent({ @@ -1333,7 +1330,6 @@ describe("MatrixClient", function () { } else { throw new Error("Unexpected event type or state key"); } - /* eslint-enable jest/no-conditional-expect */ }, } as Room["currentState"], } as unknown as Room; @@ -1355,7 +1351,6 @@ describe("MatrixClient", function () { const syncPromise = new Promise((resolve, reject) => { client.on(ClientEvent.Sync, function syncListener(state) { if (state === "SYNCING") { - // eslint-disable-next-line jest/no-conditional-expect expect(httpLookups.length).toEqual(0); client.removeListener(ClientEvent.Sync, syncListener); resolve(); @@ -1442,11 +1437,10 @@ describe("MatrixClient", function () { const wasPreparedPromise = new Promise((resolve) => { client.on(ClientEvent.Sync, function syncListener(state) { - /* eslint-disable jest/no-conditional-expect */ if (state === "ERROR" && httpLookups.length > 0) { expect(httpLookups.length).toEqual(2); expect(client.retryImmediately()).toBe(true); - jest.advanceTimersByTime(1); + vi.advanceTimersByTime(1); } else if (state === "PREPARED" && httpLookups.length === 0) { client.removeListener(ClientEvent.Sync, syncListener); resolve(null); @@ -1454,7 +1448,6 @@ describe("MatrixClient", function () { // unexpected state transition! expect(state).toEqual(null); } - /* eslint-enable jest/no-conditional-expect */ }); }); await client.startClient(); @@ -1476,13 +1469,11 @@ describe("MatrixClient", function () { const isSyncingPromise = new Promise((resolve) => { client.on(ClientEvent.Sync, function syncListener(state) { if (state === "ERROR" && httpLookups.length > 0) { - /* eslint-disable jest/no-conditional-expect */ expect(httpLookups.length).toEqual(1); expect(client.retryImmediately()).toBe(true); - /* eslint-enable jest/no-conditional-expect */ - jest.advanceTimersByTime(1); + vi.advanceTimersByTime(1); } else if (state === "RECONNECTING" && httpLookups.length > 0) { - jest.advanceTimersByTime(10000); + vi.advanceTimersByTime(10000); } else if (state === "SYNCING" && httpLookups.length === 0) { client.removeListener(ClientEvent.Sync, syncListener); resolve(null); @@ -1506,11 +1497,10 @@ describe("MatrixClient", function () { const wasPreparedPromise = new Promise((resolve) => { client.on(ClientEvent.Sync, function syncListener(state) { - /* eslint-disable jest/no-conditional-expect */ if (state === "ERROR" && httpLookups.length > 0) { expect(httpLookups.length).toEqual(3); expect(client.retryImmediately()).toBe(true); - jest.advanceTimersByTime(1); + vi.advanceTimersByTime(1); } else if (state === "PREPARED" && httpLookups.length === 0) { client.removeListener(ClientEvent.Sync, syncListener); resolve(null); @@ -1518,7 +1508,6 @@ describe("MatrixClient", function () { // unexpected state transition! expect(state).toEqual(null); } - /* eslint-enable jest/no-conditional-expect */ }); }); await client.startClient(); @@ -1542,7 +1531,7 @@ describe("MatrixClient", function () { done(); } // standard retry time is 5 to 10 seconds - jest.advanceTimersByTime(10000); + vi.advanceTimersByTime(10000); }; } @@ -1783,11 +1772,11 @@ describe("MatrixClient", function () { } }, } as Room["currentState"], - getThread: jest.fn(), - addPendingEvent: jest.fn(), - updatePendingEvent: jest.fn(), + getThread: vi.fn(), + addPendingEvent: vi.fn(), + updatePendingEvent: vi.fn(), reEmitter: { - reEmit: jest.fn(), + reEmit: vi.fn(), }, } as unknown as Room; @@ -1913,7 +1902,7 @@ describe("MatrixClient", function () { const mockRoom = { getMyMembership: () => KnownMembership.Join, updatePendingEvent: (event: MatrixEvent, status: EventStatus) => event.setStatus(status), - hasEncryptionStateEvent: jest.fn().mockReturnValue(true), + hasEncryptionStateEvent: vi.fn().mockReturnValue(true), } as unknown as Room; let mockCrypto: Mocked; @@ -1933,9 +1922,9 @@ describe("MatrixClient", function () { return mockRoom; }; mockCrypto = { - isEncryptionEnabledInRoom: jest.fn().mockResolvedValue(true), - encryptEvent: jest.fn(), - stop: jest.fn(), + isEncryptionEnabledInRoom: vi.fn().mockResolvedValue(true), + encryptEvent: vi.fn(), + stop: vi.fn(), } as unknown as Mocked; client.crypto = client["cryptoBackend"] = mockCrypto; }); @@ -2030,10 +2019,10 @@ describe("MatrixClient", function () { describe("read-markers and read-receipts", () => { it("setRoomReadMarkers", () => { - client.setRoomReadMarkersHttpRequest = jest.fn(); + client.setRoomReadMarkersHttpRequest = vi.fn(); const room = { - hasPendingEvent: jest.fn().mockReturnValue(false), - addLocalEchoReceipt: jest.fn(), + hasPendingEvent: vi.fn().mockReturnValue(false), + addLocalEchoReceipt: vi.fn(), } as unknown as Room; const rrEvent = new MatrixEvent({ event_id: "read_event_id" }); const rpEvent = new MatrixEvent({ event_id: "read_private_event_id" }); @@ -2101,7 +2090,7 @@ describe("MatrixClient", function () { describe("processBeaconEvents()", () => { it("does nothing when events is falsy", () => { const room = new Room(roomId, client, userId); - const roomStateProcessSpy = jest.spyOn(room.currentState, "processBeaconEvents"); + const roomStateProcessSpy = vi.spyOn(room.currentState, "processBeaconEvents"); client.processBeaconEvents(room, undefined); expect(roomStateProcessSpy).not.toHaveBeenCalled(); @@ -2109,7 +2098,7 @@ describe("MatrixClient", function () { it("does nothing when events is of length 0", () => { const room = new Room(roomId, client, userId); - const roomStateProcessSpy = jest.spyOn(room.currentState, "processBeaconEvents"); + const roomStateProcessSpy = vi.spyOn(room.currentState, "processBeaconEvents"); client.processBeaconEvents(room, []); expect(roomStateProcessSpy).not.toHaveBeenCalled(); @@ -2117,7 +2106,7 @@ describe("MatrixClient", function () { it("calls room states processBeaconEvents with events", () => { const room = new Room(roomId, client, userId); - const roomStateProcessSpy = jest.spyOn(room.currentState, "processBeaconEvents"); + const roomStateProcessSpy = vi.spyOn(room.currentState, "processBeaconEvents"); const messageEvent = testUtils.mkMessage({ room: roomId, user: userId, event: true }); const beaconEvent = makeBeaconEvent(userId); @@ -2131,7 +2120,7 @@ describe("MatrixClient", function () { describe("setRoomTopic", () => { const roomId = "!foofoofoofoofoofoo:matrix.org"; const createSendStateEventMock = (topic: string, htmlTopic?: string) => { - return jest.fn().mockImplementation((roomId: string, eventType: string, content: any, stateKey: string) => { + return vi.fn().mockImplementation((roomId: string, eventType: string, content: any, stateKey: string) => { expect(roomId).toEqual(roomId); expect(eventType).toEqual(EventType.RoomTopic); expect(content).toMatchObject(ContentHelpers.makeTopicContent(topic, htmlTopic)); @@ -2278,7 +2267,7 @@ describe("MatrixClient", function () { username: "1443779631:@user:example.com", password: "JlKfBy1QwLrO20385QyAtEyIv0=", } as unknown as ITurnServerResponse; - jest.spyOn(client, "turnServer").mockResolvedValue(turnServer); + vi.spyOn(client, "turnServer").mockResolvedValue(turnServer); const events: any[][] = []; const onTurnServers = (...args: any[]) => events.push(args); @@ -2300,7 +2289,7 @@ describe("MatrixClient", function () { it("emits an event when an error occurs", async () => { const error = new Error(":("); - jest.spyOn(client, "turnServer").mockRejectedValue(error); + vi.spyOn(client, "turnServer").mockRejectedValue(error); const events: any[][] = []; const onTurnServersError = (...args: any[]) => events.push(args); @@ -2312,7 +2301,7 @@ describe("MatrixClient", function () { it("considers 403 errors fatal", async () => { const error = { httpStatus: 403 }; - jest.spyOn(client, "turnServer").mockRejectedValue(error); + vi.spyOn(client, "turnServer").mockRejectedValue(error); const events: any[][] = []; const onTurnServersError = (...args: any[]) => events.push(args); @@ -2673,8 +2662,9 @@ describe("MatrixClient", function () { describe("delete account data", () => { afterEach(() => { - jest.spyOn(featureUtils, "buildFeatureSupportMap").mockRestore(); + vi.spyOn(featureUtils, "buildFeatureSupportMap").mockRestore(); }); + it("makes correct request when deletion is supported by server in unstable versions", async () => { const eventType = "im.vector.test"; const versionsResponse = { @@ -2683,7 +2673,7 @@ describe("MatrixClient", function () { "org.matrix.msc3391": true, }, }; - const requestSpy = jest.spyOn(client.http, "authedRequest").mockResolvedValue(versionsResponse); + const requestSpy = vi.spyOn(client.http, "authedRequest").mockResolvedValue(versionsResponse); const unstablePrefix = "/_matrix/client/unstable/org.matrix.msc3391"; const path = `/user/${encodeURIComponent(userId)}/account_data/${eventType}`; @@ -2702,8 +2692,8 @@ describe("MatrixClient", function () { // so mock the support map to fake stable support const stableSupportedDeletionMap = new Map(); stableSupportedDeletionMap.set(featureUtils.Feature.AccountDataDeletion, featureUtils.ServerSupport.Stable); - jest.spyOn(featureUtils, "buildFeatureSupportMap").mockResolvedValue(new Map()); - const requestSpy = jest.spyOn(client.http, "authedRequest").mockImplementation(() => Promise.resolve()); + vi.spyOn(featureUtils, "buildFeatureSupportMap").mockResolvedValue(new Map()); + const requestSpy = vi.spyOn(client.http, "authedRequest").mockImplementation(() => Promise.resolve()); const path = `/user/${encodeURIComponent(userId)}/account_data/${eventType}`; // populate version support @@ -2721,7 +2711,7 @@ describe("MatrixClient", function () { "org.matrix.msc3391": false, }, }; - const requestSpy = jest.spyOn(client.http, "authedRequest").mockResolvedValue(versionsResponse); + const requestSpy = vi.spyOn(client.http, "authedRequest").mockResolvedValue(versionsResponse); const path = `/user/${encodeURIComponent(userId)}/account_data/${eventType}`; // populate version support @@ -3192,9 +3182,9 @@ describe("MatrixClient", function () { beforeEach(() => { mockSecretStorage = { - getDefaultKeyId: jest.fn(), - hasKey: jest.fn(), - isStored: jest.fn(), + getDefaultKeyId: vi.fn(), + hasKey: vi.fn(), + isStored: vi.fn(), } as unknown as Mocked; client["_secretStorage"] = mockSecretStorage; }); @@ -3245,10 +3235,10 @@ describe("MatrixClient", function () { beforeEach(() => { mockCryptoBackend = { - isCrossSigningReady: jest.fn(), - bootstrapCrossSigning: jest.fn(), - isSecretStorageReady: jest.fn(), - stop: jest.fn().mockResolvedValue(undefined), + isCrossSigningReady: vi.fn(), + bootstrapCrossSigning: vi.fn(), + isSecretStorageReady: vi.fn(), + stop: vi.fn().mockResolvedValue(undefined), } as unknown as Mocked; client["cryptoBackend"] = mockCryptoBackend; }); @@ -3388,7 +3378,7 @@ describe("MatrixClient", function () { }); it("defaults limit to 30 events", async () => { - jest.spyOn(client.http, "authedRequest"); + vi.spyOn(client.http, "authedRequest"); const timeline = client.getNotifTimelineSet()!.getLiveTimeline(); await client.paginateEventTimeline(timeline, { backwards: true }); @@ -3457,7 +3447,7 @@ describe("MatrixClient", function () { data: {}, }; httpLookups = [response]; - jest.spyOn(client.http, "authedRequest").mockClear(); + vi.spyOn(client.http, "authedRequest").mockClear(); }); it("should make correct request to set pusher", async () => { @@ -3556,7 +3546,7 @@ describe("MatrixClient", function () { it("should return hashed lookup results", async () => { const ID_ACCESS_TOKEN = "hello_id_server_please_let_me_make_a_request"; - client.http.idServerRequest = jest.fn().mockImplementation((method, path, params) => { + client.http.idServerRequest = vi.fn().mockImplementation((method, path, params) => { if (method === "GET" && path === "/hash_details") { return { algorithms: ["sha256"], lookup_pepper: "carrot" }; } else if (method === "POST" && path === "/lookup") { diff --git a/spec/unit/matrixrtc/CallMembership.spec.ts b/spec/unit/matrixrtc/CallMembership.spec.ts index e330ef1d8fd..990e72736aa 100644 --- a/spec/unit/matrixrtc/CallMembership.spec.ts +++ b/spec/unit/matrixrtc/CallMembership.spec.ts @@ -14,25 +14,27 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + import { MatrixEvent } from "../../../src"; import { CallMembership, SessionMembershipData, DEFAULT_EXPIRE_DURATION } from "../../../src/matrixrtc/CallMembership"; import { membershipTemplate } from "./mocks"; function makeMockEvent(originTs = 0): MatrixEvent { return { - getTs: jest.fn().mockReturnValue(originTs), - getSender: jest.fn().mockReturnValue("@alice:example.org"), + getTs: vi.fn().mockReturnValue(originTs), + getSender: vi.fn().mockReturnValue("@alice:example.org"), } as unknown as MatrixEvent; } describe("CallMembership", () => { describe("SessionMembershipData", () => { beforeEach(() => { - jest.useFakeTimers(); + vi.useFakeTimers(); }); afterEach(() => { - jest.useRealTimers(); + vi.useRealTimers(); }); const membershipTemplate: SessionMembershipData = { @@ -77,13 +79,13 @@ describe("CallMembership", () => { it("considers memberships unexpired if local age low enough", () => { const fakeEvent = makeMockEvent(1000); - fakeEvent.getTs = jest.fn().mockReturnValue(Date.now() - (DEFAULT_EXPIRE_DURATION - 1)); + fakeEvent.getTs = vi.fn().mockReturnValue(Date.now() - (DEFAULT_EXPIRE_DURATION - 1)); expect(new CallMembership(fakeEvent, membershipTemplate).isExpired()).toEqual(false); }); it("considers memberships expired if local age large enough", () => { const fakeEvent = makeMockEvent(1000); - fakeEvent.getTs = jest.fn().mockReturnValue(Date.now() - (DEFAULT_EXPIRE_DURATION + 1)); + fakeEvent.getTs = vi.fn().mockReturnValue(Date.now() - (DEFAULT_EXPIRE_DURATION + 1)); expect(new CallMembership(fakeEvent, membershipTemplate).isExpired()).toEqual(true); }); @@ -107,15 +109,15 @@ describe("CallMembership", () => { fakeEvent = makeMockEvent(1000); membership = new CallMembership(fakeEvent!, membershipTemplate); - jest.useFakeTimers(); + vi.useFakeTimers(); }); afterEach(() => { - jest.useRealTimers(); + vi.useRealTimers(); }); it("calculates time until expiry", () => { - jest.setSystemTime(2000); + vi.setSystemTime(2000); // should be using absolute expiry time expect(membership.getMsUntilExpiry()).toEqual(DEFAULT_EXPIRE_DURATION - 1000); }); diff --git a/spec/unit/matrixrtc/LivekitFocus.spec.ts b/spec/unit/matrixrtc/LivekitFocus.spec.ts index 728d6a68de6..5d43bcfc7eb 100644 --- a/spec/unit/matrixrtc/LivekitFocus.spec.ts +++ b/spec/unit/matrixrtc/LivekitFocus.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { describe, expect, it } from "vitest"; + import { isLivekitFocus, isLivekitFocusActive, isLivekitFocusConfig } from "../../../src/matrixrtc/LivekitFocus"; describe("LivekitFocus", () => { diff --git a/spec/unit/matrixrtc/MatrixRTCSession.spec.ts b/spec/unit/matrixrtc/MatrixRTCSession.spec.ts index 801c2f145fc..71854f8e8d9 100644 --- a/spec/unit/matrixrtc/MatrixRTCSession.spec.ts +++ b/spec/unit/matrixrtc/MatrixRTCSession.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + import { encodeBase64, EventType, MatrixClient, MatrixError, MatrixEvent, Room } from "../../../src"; import { KnownMembership } from "../../../src/@types/membership"; import { DEFAULT_EXPIRE_DURATION, SessionMembershipData } from "../../../src/matrixrtc/CallMembership"; @@ -33,8 +35,8 @@ describe("MatrixRTCSession", () => { beforeEach(() => { client = new MatrixClient({ baseUrl: "base_url" }); - client.getUserId = jest.fn().mockReturnValue("@alice:example.org"); - client.getDeviceId = jest.fn().mockReturnValue("AAAAAAA"); + client.getUserId = vi.fn().mockReturnValue("@alice:example.org"); + client.getDeviceId = vi.fn().mockReturnValue("AAAAAAA"); }); afterEach(() => { @@ -59,17 +61,17 @@ describe("MatrixRTCSession", () => { }); it("ignores expired memberships events", () => { - jest.useFakeTimers(); + vi.useFakeTimers(); const expiredMembership = Object.assign({}, membershipTemplate); expiredMembership.expires = 1000; expiredMembership.device_id = "EXPIRED"; const mockRoom = makeMockRoom([membershipTemplate, expiredMembership]); - jest.advanceTimersByTime(2000); + vi.advanceTimersByTime(2000); sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom); expect(sess?.memberships.length).toEqual(1); expect(sess?.memberships[0].deviceId).toEqual("AAAAAAA"); - jest.useRealTimers(); + vi.useRealTimers(); }); it("ignores memberships events of members not in the room", () => { @@ -80,15 +82,15 @@ describe("MatrixRTCSession", () => { }); it("honours created_ts", () => { - jest.useFakeTimers(); - jest.setSystemTime(500); + vi.useFakeTimers(); + vi.setSystemTime(500); const expiredMembership = Object.assign({}, membershipTemplate); expiredMembership.created_ts = 500; expiredMembership.expires = 1000; const mockRoom = makeMockRoom([expiredMembership]); sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom); expect(sess?.memberships[0].getAbsoluteExpiry()).toEqual(1500); - jest.useRealTimers(); + vi.useRealTimers(); }); it("returns empty session if no membership events are present", () => { @@ -100,19 +102,19 @@ describe("MatrixRTCSession", () => { it("safely ignores events with no memberships section", () => { const roomId = secureRandomString(8); const event = { - getType: jest.fn().mockReturnValue(EventType.GroupCallMemberPrefix), - getContent: jest.fn().mockReturnValue({}), - getSender: jest.fn().mockReturnValue("@mock:user.example"), - getTs: jest.fn().mockReturnValue(1000), - getLocalAge: jest.fn().mockReturnValue(0), + getType: vi.fn().mockReturnValue(EventType.GroupCallMemberPrefix), + getContent: vi.fn().mockReturnValue({}), + getSender: vi.fn().mockReturnValue("@mock:user.example"), + getTs: vi.fn().mockReturnValue(1000), + getLocalAge: vi.fn().mockReturnValue(0), }; const mockRoom = { ...makeMockRoom([]), roomId, - getLiveTimeline: jest.fn().mockReturnValue({ - getState: jest.fn().mockReturnValue({ - on: jest.fn(), - off: jest.fn(), + getLiveTimeline: vi.fn().mockReturnValue({ + getState: vi.fn().mockReturnValue({ + on: vi.fn(), + off: vi.fn(), getStateEvents: (_type: string, _stateKey: string) => [event], events: new Map([ [ @@ -135,19 +137,19 @@ describe("MatrixRTCSession", () => { it("safely ignores events with junk memberships section", () => { const roomId = secureRandomString(8); const event = { - getType: jest.fn().mockReturnValue(EventType.GroupCallMemberPrefix), - getContent: jest.fn().mockReturnValue({ memberships: ["i am a fish"] }), - getSender: jest.fn().mockReturnValue("@mock:user.example"), - getTs: jest.fn().mockReturnValue(1000), - getLocalAge: jest.fn().mockReturnValue(0), + getType: vi.fn().mockReturnValue(EventType.GroupCallMemberPrefix), + getContent: vi.fn().mockReturnValue({ memberships: ["i am a fish"] }), + getSender: vi.fn().mockReturnValue("@mock:user.example"), + getTs: vi.fn().mockReturnValue(1000), + getLocalAge: vi.fn().mockReturnValue(0), }; const mockRoom = { ...makeMockRoom([]), roomId, - getLiveTimeline: jest.fn().mockReturnValue({ - getState: jest.fn().mockReturnValue({ - on: jest.fn(), - off: jest.fn(), + getLiveTimeline: vi.fn().mockReturnValue({ + getState: vi.fn().mockReturnValue({ + on: vi.fn(), + off: vi.fn(), getStateEvents: (_type: string, _stateKey: string) => [event], events: new Map([ [ @@ -221,10 +223,10 @@ describe("MatrixRTCSession", () => { beforeEach(() => { sentStateEvent = new Promise((resolve) => { - sendStateEventMock = jest.fn(resolve); + sendStateEventMock = vi.fn(resolve); }); sentDelayedState = new Promise((resolve) => { - sendDelayedStateMock = jest.fn(() => { + sendDelayedStateMock = vi.fn(() => { resolve(); return { delay_id: "id", @@ -254,8 +256,8 @@ describe("MatrixRTCSession", () => { describe("getOldestMembership", () => { it("returns the oldest membership event", () => { - jest.useFakeTimers(); - jest.setSystemTime(4000); + vi.useFakeTimers(); + vi.setSystemTime(4000); const mockRoom = makeMockRoom([ Object.assign({}, membershipTemplate, { device_id: "foo", created_ts: 3000 }), Object.assign({}, membershipTemplate, { device_id: "old", created_ts: 1000 }), @@ -264,7 +266,7 @@ describe("MatrixRTCSession", () => { sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom); expect(sess.getOldestMembership()!.deviceId).toEqual("old"); - jest.useRealTimers(); + vi.useRealTimers(); }); }); @@ -275,8 +277,8 @@ describe("MatrixRTCSession", () => { livekit_alias: "!active:active.url", }; it("gets the correct active focus with oldest_membership", () => { - jest.useFakeTimers(); - jest.setSystemTime(3000); + vi.useFakeTimers(); + vi.setSystemTime(3000); const mockRoom = makeMockRoom([ Object.assign({}, membershipTemplate, { device_id: "foo", @@ -294,7 +296,7 @@ describe("MatrixRTCSession", () => { focus_selection: "oldest_membership", }); expect(sess.getActiveFocus()).toBe(firstPreferredFocus); - jest.useRealTimers(); + vi.useRealTimers(); }); it("does not provide focus if the selection method is unknown", () => { const mockRoom = makeMockRoom([ @@ -330,10 +332,10 @@ describe("MatrixRTCSession", () => { beforeEach(() => { sentStateEvent = new Promise((resolve) => { - sendStateEventMock = jest.fn(resolve); + sendStateEventMock = vi.fn(resolve); }); sentDelayedState = new Promise((resolve) => { - sendDelayedStateMock = jest.fn(() => { + sendDelayedStateMock = vi.fn(() => { resolve(); return { delay_id: "id", @@ -341,9 +343,9 @@ describe("MatrixRTCSession", () => { }); }); updatedDelayedEvent = new Promise((r) => { - updateDelayedEventMock = jest.fn(r); + updateDelayedEventMock = vi.fn(r); }); - sendEventMock = jest.fn(); + sendEventMock = vi.fn(); client.sendStateEvent = sendStateEventMock; client._unstable_sendDelayedStateEvent = sendDelayedStateMock; client.sendEvent = sendEventMock; @@ -374,7 +376,7 @@ describe("MatrixRTCSession", () => { it("sends a membership event when joining a call", async () => { const realSetTimeout = setTimeout; - jest.useFakeTimers(); + vi.useFakeTimers(); sess!.joinRoomSession([mockFocus], mockFocus); await Promise.race([sentStateEvent, new Promise((resolve) => realSetTimeout(resolve, 500))]); expect(client.sendStateEvent).toHaveBeenCalledWith( @@ -403,12 +405,12 @@ describe("MatrixRTCSession", () => { expect(client._unstable_sendDelayedStateEvent).toHaveBeenCalledTimes(1); // This returns no error so we do not check if we reschedule the event again. this is done in another test. - jest.useRealTimers(); + vi.useRealTimers(); }); it("uses membershipExpiryTimeout from join config", async () => { const realSetTimeout = setTimeout; - jest.useFakeTimers(); + vi.useFakeTimers(); sess!.joinRoomSession([mockFocus], mockFocus, { membershipExpiryTimeout: 60000 }); await Promise.race([sentStateEvent, new Promise((resolve) => realSetTimeout(resolve, 500))]); expect(client.sendStateEvent).toHaveBeenCalledWith( @@ -430,7 +432,7 @@ describe("MatrixRTCSession", () => { ); await Promise.race([sentDelayedState, new Promise((resolve) => realSetTimeout(resolve, 500))]); expect(client._unstable_sendDelayedStateEvent).toHaveBeenCalledTimes(1); - jest.useRealTimers(); + vi.useRealTimers(); }); describe("calls", () => { @@ -439,10 +441,10 @@ describe("MatrixRTCSession", () => { async function testJoin(useOwnedStateEvents: boolean): Promise { if (useOwnedStateEvents) { - mockRoom.getVersion = jest.fn().mockReturnValue("org.matrix.msc3757.default"); + mockRoom.getVersion = vi.fn().mockReturnValue("org.matrix.msc3757.default"); } - jest.useFakeTimers(); + vi.useFakeTimers(); // preparing the delayed disconnect should handle the delay being too long const sendDelayedStateExceedAttempt = new Promise((resolve) => { @@ -494,10 +496,10 @@ describe("MatrixRTCSession", () => { expect(client._unstable_sendDelayedStateEvent).toHaveBeenNthCalledWith(1, ...callProps(9000)); expect(client._unstable_sendDelayedStateEvent).toHaveBeenNthCalledWith(2, ...callProps(7500)); - jest.advanceTimersByTime(5000); + vi.advanceTimersByTime(5000); await sendStateEventAttempt.then(); // needed to resolve after resendIfRateLimited catches - jest.advanceTimersByTime(1000); + vi.advanceTimersByTime(1000); await sentStateEvent; expect(client.sendStateEvent).toHaveBeenCalledWith( @@ -522,11 +524,11 @@ describe("MatrixRTCSession", () => { // ensures that we reach the code that schedules the timeout for the next delay update before we advance the timers. await flushPromises(); - jest.advanceTimersByTime(5000); + vi.advanceTimersByTime(5000); // should update delayed disconnect expect(client._unstable_updateDelayedEvent).toHaveBeenCalledTimes(2); - jest.useRealTimers(); + vi.useRealTimers(); } it("sends a membership event with session payload when joining a call", async () => { @@ -553,7 +555,7 @@ describe("MatrixRTCSession", () => { const mockRoom = makeMockRoom(membershipTemplate); sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom); - const onMembershipsChanged = jest.fn(); + const onMembershipsChanged = vi.fn(); sess.on(MatrixRTCSessionEvent.MembershipsChanged, onMembershipsChanged); sess.onRTCSessionMemberUpdate(); @@ -564,10 +566,10 @@ describe("MatrixRTCSession", () => { const mockRoom = makeMockRoom(membershipTemplate); sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom); - const onMembershipsChanged = jest.fn(); + const onMembershipsChanged = vi.fn(); sess.on(MatrixRTCSessionEvent.MembershipsChanged, onMembershipsChanged); - mockRoom.getLiveTimeline().getState = jest.fn().mockReturnValue(makeMockRoomState([], mockRoom.roomId)); + mockRoom.getLiveTimeline().getState = vi.fn().mockReturnValue(makeMockRoomState([], mockRoom.roomId)); sess.onRTCSessionMemberUpdate(); expect(onMembershipsChanged).toHaveBeenCalled(); @@ -584,10 +586,10 @@ describe("MatrixRTCSession", () => { // sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom); // const membershipObject = sess.memberships[0]; - // const onMembershipsChanged = jest.fn(); + // const onMembershipsChanged = vi.fn(); // sess.on(MatrixRTCSessionEvent.MembershipsChanged, onMembershipsChanged); - // jest.advanceTimersByTime(61 * 1000 * 1000); + // vi.advanceTimersByTime(61 * 1000 * 1000); // expect(onMembershipsChanged).toHaveBeenCalledWith([membershipObject], []); // expect(sess?.memberships.length).toEqual(0); @@ -605,9 +607,9 @@ describe("MatrixRTCSession", () => { let sendEventMock: jest.Mock; beforeEach(() => { - sendStateEventMock = jest.fn(); - sendDelayedStateMock = jest.fn(); - sendEventMock = jest.fn(); + sendStateEventMock = vi.fn(); + sendDelayedStateMock = vi.fn(); + sendEventMock = vi.fn(); client.sendStateEvent = sendStateEventMock; client._unstable_sendDelayedStateEvent = sendDelayedStateMock; client.sendEvent = sendEventMock; @@ -623,7 +625,7 @@ describe("MatrixRTCSession", () => { it("creates a key when joining", () => { sess!.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true }); - const encryptionKeyChangedListener = jest.fn(); + const encryptionKeyChangedListener = vi.fn(); sess!.on(MatrixRTCSessionEvent.EncryptionKeyChanged, encryptionKeyChangedListener); sess?.reemitEncryptionKeys(); expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(1); @@ -635,7 +637,7 @@ describe("MatrixRTCSession", () => { }); it("sends keys when joining", async () => { - jest.useFakeTimers(); + vi.useFakeTimers(); try { const eventSentPromise = new Promise((resolve) => { sendEventMock.mockImplementation(resolve); @@ -662,13 +664,13 @@ describe("MatrixRTCSession", () => { ); expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(1); } finally { - jest.useRealTimers(); + vi.useRealTimers(); } }); it("does not send key if join called when already joined", async () => { const sentStateEvent = new Promise((resolve) => { - sendStateEventMock = jest.fn(resolve); + sendStateEventMock = vi.fn(resolve); }); client.sendStateEvent = sendStateEventMock; sess!.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true }); @@ -684,14 +686,14 @@ describe("MatrixRTCSession", () => { }); it("retries key sends", async () => { - jest.useFakeTimers(); + vi.useFakeTimers(); let firstEventSent = false; try { const eventSentPromise = new Promise((resolve) => { sendEventMock.mockImplementation(() => { if (!firstEventSent) { - jest.advanceTimersByTime(10000); + vi.advanceTimersByTime(10000); firstEventSent = true; const e = new Error() as MatrixError; @@ -704,21 +706,21 @@ describe("MatrixRTCSession", () => { }); sess!.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true }); - jest.advanceTimersByTime(10000); + vi.advanceTimersByTime(10000); await eventSentPromise; expect(sendEventMock).toHaveBeenCalledTimes(2); expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(2); } finally { - jest.useRealTimers(); + vi.useRealTimers(); } }); it("cancels key send event that fail", async () => { const eventSentinel = {} as unknown as MatrixEvent; - client.cancelPendingEvent = jest.fn(); + client.cancelPendingEvent = vi.fn(); sendEventMock.mockImplementation(() => { const e = new Error() as MatrixError; e.data = {}; @@ -732,7 +734,7 @@ describe("MatrixRTCSession", () => { }); it("re-sends key if a new member joins even if a key rotation is in progress", async () => { - jest.useFakeTimers(); + vi.useFakeTimers(); try { // session with two members const member2 = Object.assign({}, membershipTemplate, { @@ -754,7 +756,7 @@ describe("MatrixRTCSession", () => { expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(1); // member2 leaves triggering key rotation - mockRoom.getLiveTimeline().getState = jest + mockRoom.getLiveTimeline().getState = vi .fn() .mockReturnValue(makeMockRoomState([membershipTemplate], mockRoom.roomId)); sess.onRTCSessionMemberUpdate(); @@ -763,12 +765,12 @@ describe("MatrixRTCSession", () => { const keysSentPromise2 = new Promise((resolve) => { sendEventMock.mockImplementation((_roomId, _evType, payload) => resolve(payload)); }); - mockRoom.getLiveTimeline().getState = jest + mockRoom.getLiveTimeline().getState = vi .fn() .mockReturnValue(makeMockRoomState([membershipTemplate, member2], mockRoom.roomId)); sess.onRTCSessionMemberUpdate(); // but, that immediate resend is throttled so we need to wait a bit - jest.advanceTimersByTime(1000); + vi.advanceTimersByTime(1000); const { keys } = await keysSentPromise2; expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(2); // key index should still be the original: 0 @@ -778,18 +780,18 @@ describe("MatrixRTCSession", () => { const keysSentPromise3 = new Promise((resolve) => { sendEventMock.mockImplementation((_roomId, _evType, payload) => resolve(payload)); }); - jest.advanceTimersByTime(2000); + vi.advanceTimersByTime(2000); const { keys: rotatedKeys } = await keysSentPromise3; expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(3); // key index should now be the rotated one: 1 expect(rotatedKeys[0].index).toEqual(1); } finally { - jest.useRealTimers(); + vi.useRealTimers(); } }); it("re-sends key if a new member joins", async () => { - jest.useFakeTimers(); + vi.useFakeTimers(); try { const mockRoom = makeMockRoom([membershipTemplate]); sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom); @@ -803,20 +805,20 @@ describe("MatrixRTCSession", () => { expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(1); sendEventMock.mockClear(); - jest.advanceTimersByTime(10000); + vi.advanceTimersByTime(10000); const keysSentPromise2 = new Promise((resolve) => { sendEventMock.mockImplementation((_roomId, _evType, payload) => resolve(payload)); }); - const onMembershipsChanged = jest.fn(); + const onMembershipsChanged = vi.fn(); sess.on(MatrixRTCSessionEvent.MembershipsChanged, onMembershipsChanged); const member2 = Object.assign({}, membershipTemplate, { device_id: "BBBBBBB", }); - mockRoom.getLiveTimeline().getState = jest + mockRoom.getLiveTimeline().getState = vi .fn() .mockReturnValue(makeMockRoomState([membershipTemplate, member2], mockRoom.roomId)); sess.onRTCSessionMemberUpdate(); @@ -826,12 +828,12 @@ describe("MatrixRTCSession", () => { expect(sendEventMock).toHaveBeenCalled(); expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(2); } finally { - jest.useRealTimers(); + vi.useRealTimers(); } }); it("does not re-send key if memberships stays same", async () => { - jest.useFakeTimers(); + vi.useFakeTimers(); try { const keysSentPromise1 = new Promise((resolve) => { sendEventMock.mockImplementation(resolve); @@ -843,7 +845,7 @@ describe("MatrixRTCSession", () => { }); const mockRoom = makeMockRoom([member1, member2]); - mockRoom.getLiveTimeline().getState = jest + mockRoom.getLiveTimeline().getState = vi .fn() .mockReturnValue(makeMockRoomState([member1, member2], mockRoom.roomId)); @@ -877,13 +879,13 @@ describe("MatrixRTCSession", () => { expect(sendEventMock).toHaveBeenCalledTimes(0); expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(1); } finally { - jest.useRealTimers(); + vi.useRealTimers(); } }); it("re-sends key if a member changes created_ts", async () => { - jest.useFakeTimers(); - jest.setSystemTime(1000); + vi.useFakeTimers(); + vi.setSystemTime(1000); try { const keysSentPromise1 = new Promise((resolve) => { sendEventMock.mockImplementation(resolve); @@ -897,7 +899,7 @@ describe("MatrixRTCSession", () => { }; const mockRoom = makeMockRoom([member1, member2]); - mockRoom.getLiveTimeline().getState = jest + mockRoom.getLiveTimeline().getState = vi .fn() .mockReturnValue(makeMockRoomState([member1, member2], mockRoom.roomId)); @@ -931,7 +933,7 @@ describe("MatrixRTCSession", () => { expect(sendEventMock).toHaveBeenCalledTimes(0); // advance time to avoid key throttling - jest.advanceTimersByTime(10000); + vi.advanceTimersByTime(10000); // update created_ts member2.created_ts = 5000; @@ -962,12 +964,12 @@ describe("MatrixRTCSession", () => { ); expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(2); } finally { - jest.useRealTimers(); + vi.useRealTimers(); } }); it("rotates key if a member leaves", async () => { - jest.useFakeTimers(); + vi.useFakeTimers(); try { const member2 = Object.assign({}, membershipTemplate, { device_id: "BBBBBBB", @@ -975,7 +977,7 @@ describe("MatrixRTCSession", () => { const mockRoom = makeMockRoom([membershipTemplate, member2]); sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom); - const onMyEncryptionKeyChanged = jest.fn(); + const onMyEncryptionKeyChanged = vi.fn(); sess.on( MatrixRTCSessionEvent.EncryptionKeyChanged, (_key: Uint8Array, _idx: number, participantId: string) => { @@ -1001,12 +1003,12 @@ describe("MatrixRTCSession", () => { sendEventMock.mockImplementation((_roomId, _evType, payload) => resolve(payload)); }); - mockRoom.getLiveTimeline().getState = jest + mockRoom.getLiveTimeline().getState = vi .fn() .mockReturnValue(makeMockRoomState([membershipTemplate], mockRoom.roomId)); sess.onRTCSessionMemberUpdate(); - jest.advanceTimersByTime(10000); + vi.advanceTimersByTime(10000); const secondKeysPayload = await keysSentPromise2; @@ -1015,7 +1017,7 @@ describe("MatrixRTCSession", () => { expect(onMyEncryptionKeyChanged).toHaveBeenCalledTimes(2); expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(2); } finally { - jest.useRealTimers(); + vi.useRealTimers(); } }); @@ -1026,7 +1028,7 @@ describe("MatrixRTCSession", () => { for (let i = 0; i < membersToTest; i++) { members.push(Object.assign({}, membershipTemplate, { device_id: `DEVICE${i}` })); } - jest.useFakeTimers(); + vi.useFakeTimers(); try { // start with all members const mockRoom = makeMockRoom(members); @@ -1042,7 +1044,7 @@ describe("MatrixRTCSession", () => { sess.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true }); } else { // otherwise update the state reducing the membership each time in order to trigger key rotation - mockRoom.getLiveTimeline().getState = jest + mockRoom.getLiveTimeline().getState = vi .fn() .mockReturnValue( makeMockRoomState(members.slice(0, membersToTest - i), mockRoom.roomId), @@ -1052,20 +1054,20 @@ describe("MatrixRTCSession", () => { sess!.onRTCSessionMemberUpdate(); // advance time to avoid key throttling - jest.advanceTimersByTime(10000); + vi.advanceTimersByTime(10000); const keysPayload = await keysSentPromise; expect(keysPayload.keys).toHaveLength(1); expect(keysPayload.keys[0].index).toEqual(i % 256); } } finally { - jest.useRealTimers(); + vi.useRealTimers(); } }); it("doesn't re-send key immediately", async () => { const realSetTimeout = setTimeout; - jest.useFakeTimers(); + vi.useFakeTimers(); try { const mockRoom = makeMockRoom([membershipTemplate]); sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom); @@ -1080,14 +1082,14 @@ describe("MatrixRTCSession", () => { sendEventMock.mockClear(); expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(1); - const onMembershipsChanged = jest.fn(); + const onMembershipsChanged = vi.fn(); sess.on(MatrixRTCSessionEvent.MembershipsChanged, onMembershipsChanged); const member2 = Object.assign({}, membershipTemplate, { device_id: "BBBBBBB", }); - mockRoom.getLiveTimeline().getState = jest + mockRoom.getLiveTimeline().getState = vi .fn() .mockReturnValue(makeMockRoomState([membershipTemplate, member2], mockRoom.roomId)); sess.onRTCSessionMemberUpdate(); @@ -1099,7 +1101,7 @@ describe("MatrixRTCSession", () => { expect(sendEventMock).not.toHaveBeenCalled(); expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(1); } finally { - jest.useRealTimers(); + vi.useRealTimers(); } }); }); @@ -1109,8 +1111,8 @@ describe("MatrixRTCSession", () => { const mockRoom = makeMockRoom([membershipTemplate]); sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom); sess.onCallEncryption({ - getType: jest.fn().mockReturnValue("io.element.call.encryption_keys"), - getContent: jest.fn().mockReturnValue({ + getType: vi.fn().mockReturnValue("io.element.call.encryption_keys"), + getContent: vi.fn().mockReturnValue({ device_id: "bobsphone", call_id: "", keys: [ @@ -1120,11 +1122,11 @@ describe("MatrixRTCSession", () => { }, ], }), - getSender: jest.fn().mockReturnValue("@bob:example.org"), - getTs: jest.fn().mockReturnValue(Date.now()), + getSender: vi.fn().mockReturnValue("@bob:example.org"), + getTs: vi.fn().mockReturnValue(Date.now()), } as unknown as MatrixEvent); - const encryptionKeyChangedListener = jest.fn(); + const encryptionKeyChangedListener = vi.fn(); sess!.on(MatrixRTCSessionEvent.EncryptionKeyChanged, encryptionKeyChangedListener); sess!.reemitEncryptionKeys(); expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(1); @@ -1141,8 +1143,8 @@ describe("MatrixRTCSession", () => { const mockRoom = makeMockRoom([membershipTemplate]); sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom); sess.onCallEncryption({ - getType: jest.fn().mockReturnValue("io.element.call.encryption_keys"), - getContent: jest.fn().mockReturnValue({ + getType: vi.fn().mockReturnValue("io.element.call.encryption_keys"), + getContent: vi.fn().mockReturnValue({ device_id: "bobsphone", call_id: "", keys: [ @@ -1152,11 +1154,11 @@ describe("MatrixRTCSession", () => { }, ], }), - getSender: jest.fn().mockReturnValue("@bob:example.org"), - getTs: jest.fn().mockReturnValue(Date.now()), + getSender: vi.fn().mockReturnValue("@bob:example.org"), + getTs: vi.fn().mockReturnValue(Date.now()), } as unknown as MatrixEvent); - const encryptionKeyChangedListener = jest.fn(); + const encryptionKeyChangedListener = vi.fn(); sess!.on(MatrixRTCSessionEvent.EncryptionKeyChanged, encryptionKeyChangedListener); sess!.reemitEncryptionKeys(); expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(1); @@ -1173,8 +1175,8 @@ describe("MatrixRTCSession", () => { const mockRoom = makeMockRoom([membershipTemplate]); sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom); sess.onCallEncryption({ - getType: jest.fn().mockReturnValue("io.element.call.encryption_keys"), - getContent: jest.fn().mockReturnValue({ + getType: vi.fn().mockReturnValue("io.element.call.encryption_keys"), + getContent: vi.fn().mockReturnValue({ device_id: "bobsphone", call_id: "", keys: [ @@ -1184,11 +1186,11 @@ describe("MatrixRTCSession", () => { }, ], }), - getSender: jest.fn().mockReturnValue("@bob:example.org"), - getTs: jest.fn().mockReturnValue(Date.now()), + getSender: vi.fn().mockReturnValue("@bob:example.org"), + getTs: vi.fn().mockReturnValue(Date.now()), } as unknown as MatrixEvent); - const encryptionKeyChangedListener = jest.fn(); + const encryptionKeyChangedListener = vi.fn(); sess!.on(MatrixRTCSessionEvent.EncryptionKeyChanged, encryptionKeyChangedListener); sess!.reemitEncryptionKeys(); expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(1); @@ -1201,8 +1203,8 @@ describe("MatrixRTCSession", () => { expect(sess!.statistics.counters.roomEventEncryptionKeysReceived).toEqual(1); sess.onCallEncryption({ - getType: jest.fn().mockReturnValue("io.element.call.encryption_keys"), - getContent: jest.fn().mockReturnValue({ + getType: vi.fn().mockReturnValue("io.element.call.encryption_keys"), + getContent: vi.fn().mockReturnValue({ device_id: "bobsphone", call_id: "", keys: [ @@ -1212,8 +1214,8 @@ describe("MatrixRTCSession", () => { }, ], }), - getSender: jest.fn().mockReturnValue("@bob:example.org"), - getTs: jest.fn().mockReturnValue(Date.now()), + getSender: vi.fn().mockReturnValue("@bob:example.org"), + getTs: vi.fn().mockReturnValue(Date.now()), } as unknown as MatrixEvent); encryptionKeyChangedListener.mockClear(); @@ -1237,8 +1239,8 @@ describe("MatrixRTCSession", () => { const mockRoom = makeMockRoom([membershipTemplate]); sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom); sess.onCallEncryption({ - getType: jest.fn().mockReturnValue("io.element.call.encryption_keys"), - getContent: jest.fn().mockReturnValue({ + getType: vi.fn().mockReturnValue("io.element.call.encryption_keys"), + getContent: vi.fn().mockReturnValue({ device_id: "bobsphone", call_id: "", keys: [ @@ -1248,13 +1250,13 @@ describe("MatrixRTCSession", () => { }, ], }), - getSender: jest.fn().mockReturnValue("@bob:example.org"), - getTs: jest.fn().mockReturnValue(2000), + getSender: vi.fn().mockReturnValue("@bob:example.org"), + getTs: vi.fn().mockReturnValue(2000), } as unknown as MatrixEvent); sess.onCallEncryption({ - getType: jest.fn().mockReturnValue("io.element.call.encryption_keys"), - getContent: jest.fn().mockReturnValue({ + getType: vi.fn().mockReturnValue("io.element.call.encryption_keys"), + getContent: vi.fn().mockReturnValue({ device_id: "bobsphone", call_id: "", keys: [ @@ -1264,11 +1266,11 @@ describe("MatrixRTCSession", () => { }, ], }), - getSender: jest.fn().mockReturnValue("@bob:example.org"), - getTs: jest.fn().mockReturnValue(1000), // earlier timestamp than the newer key + getSender: vi.fn().mockReturnValue("@bob:example.org"), + getTs: vi.fn().mockReturnValue(1000), // earlier timestamp than the newer key } as unknown as MatrixEvent); - const encryptionKeyChangedListener = jest.fn(); + const encryptionKeyChangedListener = vi.fn(); sess!.on(MatrixRTCSessionEvent.EncryptionKeyChanged, encryptionKeyChangedListener); sess!.reemitEncryptionKeys(); expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(1); @@ -1285,8 +1287,8 @@ describe("MatrixRTCSession", () => { const mockRoom = makeMockRoom([membershipTemplate]); sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom); sess.onCallEncryption({ - getType: jest.fn().mockReturnValue("io.element.call.encryption_keys"), - getContent: jest.fn().mockReturnValue({ + getType: vi.fn().mockReturnValue("io.element.call.encryption_keys"), + getContent: vi.fn().mockReturnValue({ device_id: "bobsphone", call_id: "", keys: [ @@ -1296,13 +1298,13 @@ describe("MatrixRTCSession", () => { }, ], }), - getSender: jest.fn().mockReturnValue("@bob:example.org"), - getTs: jest.fn().mockReturnValue(1000), + getSender: vi.fn().mockReturnValue("@bob:example.org"), + getTs: vi.fn().mockReturnValue(1000), } as unknown as MatrixEvent); sess.onCallEncryption({ - getType: jest.fn().mockReturnValue("io.element.call.encryption_keys"), - getContent: jest.fn().mockReturnValue({ + getType: vi.fn().mockReturnValue("io.element.call.encryption_keys"), + getContent: vi.fn().mockReturnValue({ device_id: "bobsphone", call_id: "", keys: [ @@ -1312,11 +1314,11 @@ describe("MatrixRTCSession", () => { }, ], }), - getSender: jest.fn().mockReturnValue("@bob:example.org"), - getTs: jest.fn().mockReturnValue(1000), // same timestamp as the first key + getSender: vi.fn().mockReturnValue("@bob:example.org"), + getTs: vi.fn().mockReturnValue(1000), // same timestamp as the first key } as unknown as MatrixEvent); - const encryptionKeyChangedListener = jest.fn(); + const encryptionKeyChangedListener = vi.fn(); sess!.on(MatrixRTCSessionEvent.EncryptionKeyChanged, encryptionKeyChangedListener); sess!.reemitEncryptionKeys(); expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(1); @@ -1331,8 +1333,8 @@ describe("MatrixRTCSession", () => { const mockRoom = makeMockRoom([membershipTemplate]); sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom); sess.onCallEncryption({ - getType: jest.fn().mockReturnValue("io.element.call.encryption_keys"), - getContent: jest.fn().mockReturnValue({ + getType: vi.fn().mockReturnValue("io.element.call.encryption_keys"), + getContent: vi.fn().mockReturnValue({ device_id: client.getDeviceId(), call_id: "", keys: [ @@ -1342,11 +1344,11 @@ describe("MatrixRTCSession", () => { }, ], }), - getSender: jest.fn().mockReturnValue(client.getUserId()), - getTs: jest.fn().mockReturnValue(Date.now()), + getSender: vi.fn().mockReturnValue(client.getUserId()), + getTs: vi.fn().mockReturnValue(Date.now()), } as unknown as MatrixEvent); - const encryptionKeyChangedListener = jest.fn(); + const encryptionKeyChangedListener = vi.fn(); sess!.on(MatrixRTCSessionEvent.EncryptionKeyChanged, encryptionKeyChangedListener); sess!.reemitEncryptionKeys(); expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(0); @@ -1355,16 +1357,16 @@ describe("MatrixRTCSession", () => { }); it("tracks total age statistics for collected keys", () => { - jest.useFakeTimers(); + vi.useFakeTimers(); try { const mockRoom = makeMockRoom([membershipTemplate]); sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom); // defaults to getTs() - jest.setSystemTime(1000); + vi.setSystemTime(1000); sess.onCallEncryption({ - getType: jest.fn().mockReturnValue("io.element.call.encryption_keys"), - getContent: jest.fn().mockReturnValue({ + getType: vi.fn().mockReturnValue("io.element.call.encryption_keys"), + getContent: vi.fn().mockReturnValue({ device_id: "bobsphone", call_id: "", keys: [ @@ -1374,15 +1376,15 @@ describe("MatrixRTCSession", () => { }, ], }), - getSender: jest.fn().mockReturnValue("@bob:example.org"), - getTs: jest.fn().mockReturnValue(0), + getSender: vi.fn().mockReturnValue("@bob:example.org"), + getTs: vi.fn().mockReturnValue(0), } as unknown as MatrixEvent); expect(sess!.statistics.totals.roomEventEncryptionKeysReceivedTotalAge).toEqual(1000); - jest.setSystemTime(2000); + vi.setSystemTime(2000); sess.onCallEncryption({ - getType: jest.fn().mockReturnValue("io.element.call.encryption_keys"), - getContent: jest.fn().mockReturnValue({ + getType: vi.fn().mockReturnValue("io.element.call.encryption_keys"), + getContent: vi.fn().mockReturnValue({ device_id: "bobsphone", call_id: "", keys: [ @@ -1393,15 +1395,15 @@ describe("MatrixRTCSession", () => { ], sent_ts: 0, }), - getSender: jest.fn().mockReturnValue("@bob:example.org"), - getTs: jest.fn().mockReturnValue(Date.now()), + getSender: vi.fn().mockReturnValue("@bob:example.org"), + getTs: vi.fn().mockReturnValue(Date.now()), } as unknown as MatrixEvent); expect(sess!.statistics.totals.roomEventEncryptionKeysReceivedTotalAge).toEqual(3000); - jest.setSystemTime(3000); + vi.setSystemTime(3000); sess.onCallEncryption({ - getType: jest.fn().mockReturnValue("io.element.call.encryption_keys"), - getContent: jest.fn().mockReturnValue({ + getType: vi.fn().mockReturnValue("io.element.call.encryption_keys"), + getContent: vi.fn().mockReturnValue({ device_id: "bobsphone", call_id: "", keys: [ @@ -1412,12 +1414,12 @@ describe("MatrixRTCSession", () => { ], sent_ts: 1000, }), - getSender: jest.fn().mockReturnValue("@bob:example.org"), - getTs: jest.fn().mockReturnValue(Date.now()), + getSender: vi.fn().mockReturnValue("@bob:example.org"), + getTs: vi.fn().mockReturnValue(Date.now()), } as unknown as MatrixEvent); expect(sess!.statistics.totals.roomEventEncryptionKeysReceivedTotalAge).toEqual(5000); } finally { - jest.useRealTimers(); + vi.useRealTimers(); } }); }); diff --git a/spec/unit/matrixrtc/MatrixRTCSessionManager.spec.ts b/spec/unit/matrixrtc/MatrixRTCSessionManager.spec.ts index 5b87098c0f8..cc6424fd6e1 100644 --- a/spec/unit/matrixrtc/MatrixRTCSessionManager.spec.ts +++ b/spec/unit/matrixrtc/MatrixRTCSessionManager.spec.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { Mock } from "jest-mock"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { ClientEvent, @@ -28,6 +28,7 @@ import { import { RoomStateEvent } from "../../../src/models/room-state"; import { MatrixRTCSessionManagerEvents } from "../../../src/matrixrtc/MatrixRTCSessionManager"; import { makeMockRoom, makeMockRoomState, membershipTemplate } from "./mocks"; +import { mocked } from "../../test-utils"; describe("MatrixRTCSessionManager", () => { let client: MatrixClient; @@ -43,12 +44,12 @@ describe("MatrixRTCSessionManager", () => { }); it("Fires event when session starts", () => { - const onStarted = jest.fn(); + const onStarted = vi.fn(); client.matrixRTC.on(MatrixRTCSessionManagerEvents.SessionStarted, onStarted); try { const room1 = makeMockRoom([membershipTemplate]); - jest.spyOn(client, "getRooms").mockReturnValue([room1]); + vi.spyOn(client, "getRooms").mockReturnValue([room1]); client.emit(ClientEvent.Room, room1); expect(onStarted).toHaveBeenCalledWith(room1.roomId, client.matrixRTC.getActiveRoomSession(room1)); @@ -58,16 +59,16 @@ describe("MatrixRTCSessionManager", () => { }); it("Fires event when session ends", () => { - const onEnded = jest.fn(); + const onEnded = vi.fn(); client.matrixRTC.on(MatrixRTCSessionManagerEvents.SessionEnded, onEnded); const room1 = makeMockRoom(membershipTemplate); - jest.spyOn(client, "getRooms").mockReturnValue([room1]); - jest.spyOn(client, "getRoom").mockReturnValue(room1); + vi.spyOn(client, "getRooms").mockReturnValue([room1]); + vi.spyOn(client, "getRoom").mockReturnValue(room1); client.emit(ClientEvent.Room, room1); - (room1.getLiveTimeline as Mock).mockReturnValue({ - getState: jest.fn().mockReturnValue(makeMockRoomState([{}], room1.roomId)), + mocked(room1.getLiveTimeline).mockReturnValue({ + getState: vi.fn().mockReturnValue(makeMockRoomState([{}], room1.roomId)), }); const roomState = room1.getLiveTimeline().getState(EventTimeline.FORWARDS)!; @@ -80,19 +81,19 @@ describe("MatrixRTCSessionManager", () => { it("Calls onCallEncryption on encryption keys event", async () => { const room1 = makeMockRoom([membershipTemplate]); - jest.spyOn(client, "getRooms").mockReturnValue([room1]); - jest.spyOn(client, "getRoom").mockReturnValue(room1); + vi.spyOn(client, "getRooms").mockReturnValue([room1]); + vi.spyOn(client, "getRoom").mockReturnValue(room1); client.emit(ClientEvent.Room, room1); - const onCallEncryptionMock = jest.fn(); + const onCallEncryptionMock = vi.fn(); client.matrixRTC.getRoomSession(room1).onCallEncryption = onCallEncryptionMock; client.decryptEventIfNeeded = () => Promise.resolve(); const timelineEvent = { - getType: jest.fn().mockReturnValue(EventType.CallEncryptionKeysPrefix), - getContent: jest.fn().mockReturnValue({}), - getSender: jest.fn().mockReturnValue("@mock:user.example"), - getRoomId: jest.fn().mockReturnValue("!room:id"), - isDecryptionFailure: jest.fn().mockReturnValue(false), + getType: vi.fn().mockReturnValue(EventType.CallEncryptionKeysPrefix), + getContent: vi.fn().mockReturnValue({}), + getSender: vi.fn().mockReturnValue("@mock:user.example"), + getRoomId: vi.fn().mockReturnValue("!room:id"), + isDecryptionFailure: vi.fn().mockReturnValue(false), sender: { userId: "@mock:user.example", }, @@ -105,16 +106,16 @@ describe("MatrixRTCSessionManager", () => { describe("event decryption", () => { it("Retries decryption and processes success", async () => { try { - jest.useFakeTimers(); + vi.useFakeTimers(); const room1 = makeMockRoom([membershipTemplate]); - jest.spyOn(client, "getRooms").mockReturnValue([room1]); - jest.spyOn(client, "getRoom").mockReturnValue(room1); + vi.spyOn(client, "getRooms").mockReturnValue([room1]); + vi.spyOn(client, "getRoom").mockReturnValue(room1); client.emit(ClientEvent.Room, room1); - const onCallEncryptionMock = jest.fn(); + const onCallEncryptionMock = vi.fn(); client.matrixRTC.getRoomSession(room1).onCallEncryption = onCallEncryptionMock; let isDecryptionFailure = true; - client.decryptEventIfNeeded = jest + client.decryptEventIfNeeded = vi .fn() .mockReturnValueOnce(Promise.resolve()) .mockImplementation(() => { @@ -122,12 +123,12 @@ describe("MatrixRTCSessionManager", () => { return Promise.resolve(); }); const timelineEvent = { - getType: jest.fn().mockReturnValue(EventType.CallEncryptionKeysPrefix), - getContent: jest.fn().mockReturnValue({}), - getSender: jest.fn().mockReturnValue("@mock:user.example"), - getRoomId: jest.fn().mockReturnValue("!room:id"), - isDecryptionFailure: jest.fn().mockImplementation(() => isDecryptionFailure), - getId: jest.fn().mockReturnValue("event_id"), + getType: vi.fn().mockReturnValue(EventType.CallEncryptionKeysPrefix), + getContent: vi.fn().mockReturnValue({}), + getSender: vi.fn().mockReturnValue("@mock:user.example"), + getRoomId: vi.fn().mockReturnValue("!room:id"), + isDecryptionFailure: vi.fn().mockImplementation(() => isDecryptionFailure), + getId: vi.fn().mockReturnValue("event_id"), sender: { userId: "@mock:user.example", }, @@ -138,33 +139,33 @@ describe("MatrixRTCSessionManager", () => { expect(onCallEncryptionMock).toHaveBeenCalledTimes(0); // should retry after one second: - await jest.advanceTimersByTimeAsync(1500); + await vi.advanceTimersByTimeAsync(1500); expect(client.decryptEventIfNeeded).toHaveBeenCalledTimes(2); expect(onCallEncryptionMock).toHaveBeenCalledTimes(1); } finally { - jest.useRealTimers(); + vi.useRealTimers(); } }); it("Retries decryption and processes failure", async () => { try { - jest.useFakeTimers(); + vi.useFakeTimers(); const room1 = makeMockRoom([membershipTemplate]); - jest.spyOn(client, "getRooms").mockReturnValue([room1]); - jest.spyOn(client, "getRoom").mockReturnValue(room1); + vi.spyOn(client, "getRooms").mockReturnValue([room1]); + vi.spyOn(client, "getRoom").mockReturnValue(room1); client.emit(ClientEvent.Room, room1); - const onCallEncryptionMock = jest.fn(); + const onCallEncryptionMock = vi.fn(); client.matrixRTC.getRoomSession(room1).onCallEncryption = onCallEncryptionMock; - client.decryptEventIfNeeded = jest.fn().mockReturnValue(Promise.resolve()); + client.decryptEventIfNeeded = vi.fn().mockReturnValue(Promise.resolve()); const timelineEvent = { - getType: jest.fn().mockReturnValue(EventType.CallEncryptionKeysPrefix), - getContent: jest.fn().mockReturnValue({}), - getSender: jest.fn().mockReturnValue("@mock:user.example"), - getRoomId: jest.fn().mockReturnValue("!room:id"), - isDecryptionFailure: jest.fn().mockReturnValue(true), // always fail - getId: jest.fn().mockReturnValue("event_id"), + getType: vi.fn().mockReturnValue(EventType.CallEncryptionKeysPrefix), + getContent: vi.fn().mockReturnValue({}), + getSender: vi.fn().mockReturnValue("@mock:user.example"), + getRoomId: vi.fn().mockReturnValue("!room:id"), + isDecryptionFailure: vi.fn().mockReturnValue(true), // always fail + getId: vi.fn().mockReturnValue("event_id"), sender: { userId: "@mock:user.example", }, @@ -175,18 +176,18 @@ describe("MatrixRTCSessionManager", () => { expect(onCallEncryptionMock).toHaveBeenCalledTimes(0); // should retry after one second: - await jest.advanceTimersByTimeAsync(1500); + await vi.advanceTimersByTimeAsync(1500); expect(client.decryptEventIfNeeded).toHaveBeenCalledTimes(2); expect(onCallEncryptionMock).toHaveBeenCalledTimes(0); // doesn't retry again: - await jest.advanceTimersByTimeAsync(1500); + await vi.advanceTimersByTimeAsync(1500); expect(client.decryptEventIfNeeded).toHaveBeenCalledTimes(2); expect(onCallEncryptionMock).toHaveBeenCalledTimes(0); } finally { - jest.useRealTimers(); + vi.useRealTimers(); } }); }); diff --git a/spec/unit/matrixrtc/mocks.ts b/spec/unit/matrixrtc/mocks.ts index 3b35a2faba2..c2bdbf44bfc 100644 --- a/spec/unit/matrixrtc/mocks.ts +++ b/spec/unit/matrixrtc/mocks.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { vi } from "vitest"; + import { EventType, MatrixEvent, Room } from "../../../src"; import { SessionMembershipData } from "../../../src/matrixrtc/CallMembership"; import { secureRandomString } from "../../../src/randomstring"; @@ -46,11 +48,11 @@ export function makeMockRoom(membershipData: MembershipData): Room { const roomState = makeMockRoomState(membershipData, roomId); const room = { roomId: roomId, - hasMembershipState: jest.fn().mockReturnValue(true), - getLiveTimeline: jest.fn().mockReturnValue({ - getState: jest.fn().mockReturnValue(roomState), + hasMembershipState: vi.fn().mockReturnValue(true), + getLiveTimeline: vi.fn().mockReturnValue({ + getState: vi.fn().mockReturnValue(roomState), }), - getVersion: jest.fn().mockReturnValue("default"), + getVersion: vi.fn().mockReturnValue("default"), } as unknown as Room; return room; } @@ -65,8 +67,8 @@ export function makeMockRoomState(membershipData: MembershipData, roomId: string }); return { - on: jest.fn(), - off: jest.fn(), + on: vi.fn(), + off: vi.fn(), getStateEvents: (_: string, stateKey: string) => { if (stateKey !== undefined) return keysAndEvents.find(([k]) => k === stateKey)?.[1]; return events; @@ -90,14 +92,14 @@ export function makeMockRoomState(membershipData: MembershipData, roomId: string export function mockRTCEvent(membershipData: MembershipData, roomId: string): MatrixEvent { return { - getType: jest.fn().mockReturnValue(EventType.GroupCallMemberPrefix), - getContent: jest.fn().mockReturnValue(membershipData), - getSender: jest.fn().mockReturnValue("@mock:user.example"), - getTs: jest.fn().mockReturnValue(Date.now()), - getRoomId: jest.fn().mockReturnValue(roomId), + getType: vi.fn().mockReturnValue(EventType.GroupCallMemberPrefix), + getContent: vi.fn().mockReturnValue(membershipData), + getSender: vi.fn().mockReturnValue("@mock:user.example"), + getTs: vi.fn().mockReturnValue(Date.now()), + getRoomId: vi.fn().mockReturnValue(roomId), sender: { userId: "@mock:user.example", }, - isDecryptionFailure: jest.fn().mockReturnValue(false), + isDecryptionFailure: vi.fn().mockReturnValue(false), } as unknown as MatrixEvent; } diff --git a/spec/unit/models/MSC3089Branch.spec.ts b/spec/unit/models/MSC3089Branch.spec.ts index 7151e968026..105f70d264b 100644 --- a/spec/unit/models/MSC3089Branch.spec.ts +++ b/spec/unit/models/MSC3089Branch.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { it, describe, beforeEach, expect, vi } from "vitest"; + import { IContent, MatrixClient, MatrixEvent } from "../../../src"; import { Room } from "../../../src/models/room"; import { RelationType, UNSTABLE_MSC3089_BRANCH } from "../../../src/@types/event"; @@ -85,7 +87,7 @@ describe("MSC3089Branch", () => { it("should be able to delete the file", async () => { const eventIdOrder = [fileEventId, fileEventId2]; - const stateFn = jest + const stateFn = vi .fn() .mockImplementation((roomId: string, eventType: string, content: any, stateKey: string) => { expect(roomId).toEqual(branchRoomId); @@ -98,7 +100,7 @@ describe("MSC3089Branch", () => { }); client.sendStateEvent = stateFn; - const redactFn = jest.fn().mockImplementation((roomId: string, eventId: string) => { + const redactFn = vi.fn().mockImplementation((roomId: string, eventId: string) => { expect(roomId).toEqual(branchRoomId); expect(eventId).toEqual(eventIdOrder[stateFn.mock.calls.length - 1]); @@ -127,7 +129,7 @@ describe("MSC3089Branch", () => { it("should be able to change its name", async () => { const name = "My File.txt"; indexEvent.getContent = () => ({ active: true, retained: true }); - const stateFn = jest + const stateFn = vi .fn() .mockImplementation((roomId: string, eventType: string, content: any, stateKey: string) => { expect(roomId).toEqual(branchRoomId); @@ -183,7 +185,7 @@ describe("MSC3089Branch", () => { it("should be able to change its locked status", async () => { const locked = true; indexEvent.getContent = () => ({ active: true, retained: true }); - const stateFn = jest + const stateFn = vi .fn() .mockImplementation((roomId: string, eventType: string, content: any, stateKey: string) => { expect(roomId).toEqual(branchRoomId); @@ -259,20 +261,18 @@ describe("MSC3089Branch", () => { const canaryAddl = { canary: true }; indexEvent.getContent = () => ({ active: true, retained: true }); const stateKeyOrder = [fileEventId2, fileEventId]; - const stateFn = jest + const stateFn = vi .fn() .mockImplementation((roomId: string, eventType: string, content: any, stateKey: string) => { expect(roomId).toEqual(branchRoomId); expect(eventType).toEqual(UNSTABLE_MSC3089_BRANCH.unstable); // test that we're definitely using the unstable value expect(stateKey).toEqual(stateKeyOrder[stateFn.mock.calls.length - 1]); if (stateKey === fileEventId) { - // eslint-disable-next-line jest/no-conditional-expect expect(content).toMatchObject({ retained: true, // canary for copying state active: false, }); } else if (stateKey === fileEventId2) { - // eslint-disable-next-line jest/no-conditional-expect expect(content).toMatchObject({ active: true, version: 2, @@ -286,7 +286,7 @@ describe("MSC3089Branch", () => { }); client.sendStateEvent = stateFn; - const createFn = jest + const createFn = vi .fn() .mockImplementation((name: string, contents: ArrayBuffer, info: Partial, addl: IContent) => { expect(name).toEqual(canaryName); diff --git a/spec/unit/models/MSC3089TreeSpace.spec.ts b/spec/unit/models/MSC3089TreeSpace.spec.ts index 2a07ced56cc..4f27ee1f538 100644 --- a/spec/unit/models/MSC3089TreeSpace.spec.ts +++ b/spec/unit/models/MSC3089TreeSpace.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { it, describe, expect, beforeEach, vi } from "vitest"; + import { IContent, MatrixClient } from "../../../src"; import { Room } from "../../../src/models/room"; import { MatrixEvent } from "../../../src/models/event"; @@ -85,7 +87,7 @@ describe("MSC3089TreeSpace", () => { it("should support setting the name of the space", async () => { const newName = "NEW NAME"; - const fn = jest + const fn = vi .fn() .mockImplementation((stateRoomId: string, eventType: EventType, content: any, stateKey: string) => { expect(stateRoomId).toEqual(roomId); @@ -101,7 +103,7 @@ describe("MSC3089TreeSpace", () => { it("should support inviting users to the space", async () => { const target = targetUser; - const fn = jest.fn().mockImplementation((inviteRoomId: string, userId: string) => { + const fn = vi.fn().mockImplementation((inviteRoomId: string, userId: string) => { expect(inviteRoomId).toEqual(roomId); expect(userId).toEqual(target); return Promise.resolve(); @@ -113,7 +115,7 @@ describe("MSC3089TreeSpace", () => { it("should retry invites to the space", async () => { const target = targetUser; - const fn = jest.fn().mockImplementation((inviteRoomId: string, userId: string) => { + const fn = vi.fn().mockImplementation((inviteRoomId: string, userId: string) => { expect(inviteRoomId).toEqual(roomId); expect(userId).toEqual(target); if (fn.mock.calls.length === 1) return Promise.reject(new Error("Sample Failure")); @@ -126,7 +128,7 @@ describe("MSC3089TreeSpace", () => { it("should not retry invite permission errors", async () => { const target = targetUser; - const fn = jest.fn().mockImplementation((inviteRoomId: string, userId: string) => { + const fn = vi.fn().mockImplementation((inviteRoomId: string, userId: string) => { expect(inviteRoomId).toEqual(roomId); expect(userId).toEqual(target); return Promise.reject(new MatrixError({ errcode: "M_FORBIDDEN", error: "Sample Failure" })); @@ -140,7 +142,7 @@ describe("MSC3089TreeSpace", () => { it("should invite to subspaces", async () => { const target = targetUser; - const fn = jest.fn().mockImplementation((inviteRoomId: string, userId: string) => { + const fn = vi.fn().mockImplementation((inviteRoomId: string, userId: string) => { expect(inviteRoomId).toEqual(roomId); expect(userId).toEqual(target); return Promise.resolve(); @@ -161,7 +163,7 @@ describe("MSC3089TreeSpace", () => { async function evaluatePowerLevels(pls: any, role: TreePermissions, expectedPl: number) { makePowerLevels(pls); - const fn = jest + const fn = vi .fn() .mockImplementation((stateRoomId: string, eventType: EventType, content: any, stateKey: string) => { expect(stateRoomId).toEqual(roomId); @@ -294,11 +296,11 @@ describe("MSC3089TreeSpace", () => { } }; client.getDomain = () => domain; - const createFn = jest.fn().mockImplementation(async (name: string) => { + const createFn = vi.fn().mockImplementation(async (name: string) => { expect(name).toEqual(subspaceName); return new MSC3089TreeSpace(client, subspaceId); }); - const sendStateFn = jest + const sendStateFn = vi .fn() .mockImplementation(async (roomId: string, eventType: EventType, content: any, stateKey: string) => { expect([tree.roomId, subspaceId]).toContain(roomId); @@ -352,7 +354,7 @@ describe("MSC3089TreeSpace", () => { }; client.getRoom = () => ({}) as Room; // to appease the TreeSpace constructor - const getFn = jest.fn().mockImplementation((roomId: string) => { + const getFn = vi.fn().mockImplementation((roomId: string) => { if (roomId === thirdChildRoom) { throw new Error("Mock not-a-space room case called (expected)"); } @@ -389,10 +391,10 @@ describe("MSC3089TreeSpace", () => { }); it("should be able to delete itself", async () => { - const delete1 = jest.fn().mockImplementation(() => Promise.resolve()); + const delete1 = vi.fn().mockImplementation(() => Promise.resolve()); const subdir1 = { delete: delete1 } as any as MSC3089TreeSpace; // mock tested bits - const delete2 = jest.fn().mockImplementation(() => Promise.resolve()); + const delete2 = vi.fn().mockImplementation(() => Promise.resolve()); const subdir2 = { delete: delete2 } as any as MSC3089TreeSpace; // mock tested bits const joinMemberId = "@join:example.org"; @@ -422,8 +424,8 @@ describe("MSC3089TreeSpace", () => { }; // These two functions are tested by input expectations, so no expectations in the function bodies - const kickFn = jest.fn().mockImplementation((userId) => Promise.resolve()); - const leaveFn = jest.fn().mockImplementation(() => Promise.resolve()); + const kickFn = vi.fn().mockImplementation((userId) => Promise.resolve()); + const leaveFn = vi.fn().mockImplementation(() => Promise.resolve()); client.kick = kickFn; client.leave = leaveFn; client.getUserId = () => selfUserId; @@ -449,7 +451,7 @@ describe("MSC3089TreeSpace", () => { let parentRoom: Room; let childTrees: MSC3089TreeSpace[]; let rooms: { [roomId: string]: Room }; - let clientSendStateFn: jest.MockedFunction; + let clientSendStateFn: vi.MockedFunction; const staticDomain = "static.example.org"; function addSubspace(roomId: string, createTs?: number, order?: string) { @@ -540,7 +542,7 @@ describe("MSC3089TreeSpace", () => { (tree).room = parentRoom; // override readonly client.getRoom = (r) => rooms[r ?? ""]; - clientSendStateFn = jest + clientSendStateFn = vi .fn() .mockImplementation((roomId: string, eventType: EventType, content: any, stateKey: string) => { expect(roomId).toEqual(tree.roomId); @@ -902,7 +904,7 @@ describe("MSC3089TreeSpace", () => { const fileName = "My File.txt"; const fileContents = "This is a test file"; - const uploadFn = jest.fn().mockImplementation((contents: Buffer, opts: any) => { + const uploadFn = vi.fn().mockImplementation((contents: Buffer, opts: any) => { expect(contents.length).toEqual(fileContents.length); expect(opts).toMatchObject({ includeFilename: false, @@ -911,7 +913,7 @@ describe("MSC3089TreeSpace", () => { }); client.uploadContent = uploadFn; - const sendMsgFn = jest.fn().mockImplementation((roomId: string, contents: any) => { + const sendMsgFn = vi.fn().mockImplementation((roomId: string, contents: any) => { expect(roomId).toEqual(tree.roomId); expect(contents).toMatchObject({ msgtype: MsgType.File, @@ -926,7 +928,7 @@ describe("MSC3089TreeSpace", () => { }); client.sendMessage = sendMsgFn; - const sendStateFn = jest + const sendStateFn = vi .fn() .mockImplementation((roomId: string, eventType: string, content: any, stateKey: string) => { expect(roomId).toEqual(tree.roomId); @@ -962,7 +964,7 @@ describe("MSC3089TreeSpace", () => { const fileName = "My File.txt"; const fileContents = "This is a test file"; - const uploadFn = jest.fn().mockImplementation((contents: Buffer, opts: any) => { + const uploadFn = vi.fn().mockImplementation((contents: Buffer, opts: any) => { expect(contents.length).toEqual(fileContents.length); expect(opts).toMatchObject({ includeFilename: false, @@ -971,7 +973,7 @@ describe("MSC3089TreeSpace", () => { }); client.uploadContent = uploadFn; - const sendMsgFn = jest.fn().mockImplementation((roomId: string, contents: any) => { + const sendMsgFn = vi.fn().mockImplementation((roomId: string, contents: any) => { expect(roomId).toEqual(tree.roomId); const content = { msgtype: MsgType.File, @@ -989,7 +991,7 @@ describe("MSC3089TreeSpace", () => { }); client.sendMessage = sendMsgFn; - const sendStateFn = jest + const sendStateFn = vi .fn() .mockImplementation((roomId: string, eventType: string, content: any, stateKey: string) => { expect(roomId).toEqual(tree.roomId); diff --git a/spec/unit/models/beacon.spec.ts b/spec/unit/models/beacon.spec.ts index 8c35268d81e..4df669e4bcd 100644 --- a/spec/unit/models/beacon.spec.ts +++ b/spec/unit/models/beacon.spec.ts @@ -14,13 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { afterAll, beforeEach, describe, expect, it, vi } from "vitest"; + import { REFERENCE_RELATION } from "../../../src/@types/extensible_events"; import { MatrixEvent } from "../../../src"; import { M_BEACON_INFO } from "../../../src/@types/beacon"; import { isTimestampInDuration, Beacon, BeaconEvent } from "../../../src/models/beacon"; import { makeBeaconEvent, makeBeaconInfoEvent } from "../../test-utils/beacon"; -jest.useFakeTimers(); +vi.useFakeTimers(); describe("Beacon", () => { describe("isTimestampInDuration()", () => { @@ -70,9 +72,9 @@ describe("Beacon", () => { const advanceDateAndTime = (ms: number) => { // bc liveness check uses Date.now we have to advance this mock - jest.spyOn(globalThis.Date, "now").mockReturnValue(Date.now() + ms); + vi.spyOn(globalThis.Date, "now").mockReturnValue(Date.now() + ms); // then advance time for the interval by the same amount - jest.advanceTimersByTime(ms); + vi.advanceTimersByTime(ms); }; beforeEach(() => { @@ -108,11 +110,11 @@ describe("Beacon", () => { ); // back to 'now' - jest.spyOn(globalThis.Date, "now").mockReturnValue(now); + vi.spyOn(globalThis.Date, "now").mockReturnValue(now); }); afterAll(() => { - jest.spyOn(globalThis.Date, "now").mockRestore(); + vi.spyOn(globalThis.Date, "now").mockRestore(); }); it("creates beacon from event", () => { @@ -250,7 +252,7 @@ describe("Beacon", () => { it("does not update with an older event", () => { const beacon = new Beacon(liveBeaconEvent); - const emitSpy = jest.spyOn(beacon, "emit").mockClear(); + const emitSpy = vi.spyOn(beacon, "emit").mockClear(); expect(beacon.beaconInfoId).toEqual(liveBeaconEvent.getId()); const oldUpdateEvent = makeBeaconInfoEvent(userId, roomId); @@ -265,7 +267,7 @@ describe("Beacon", () => { it("updates event", () => { const beacon = new Beacon(liveBeaconEvent); - const emitSpy = jest.spyOn(beacon, "emit"); + const emitSpy = vi.spyOn(beacon, "emit"); expect(beacon.isLive).toEqual(true); @@ -283,7 +285,7 @@ describe("Beacon", () => { it("emits livenesschange event when beacon liveness changes", () => { const beacon = new Beacon(liveBeaconEvent); - const emitSpy = jest.spyOn(beacon, "emit"); + const emitSpy = vi.spyOn(beacon, "emit"); expect(beacon.isLive).toEqual(true); @@ -305,7 +307,7 @@ describe("Beacon", () => { // beacon was created an hour ago // and has a 3hr duration const beacon = new Beacon(notLiveBeaconEvent); - const emitSpy = jest.spyOn(beacon, "emit"); + const emitSpy = vi.spyOn(beacon, "emit"); beacon.monitorLiveness(); @@ -332,7 +334,7 @@ describe("Beacon", () => { const beacon = new Beacon(futureBeaconEvent); expect(beacon.isLive).toBeFalsy(); - const emitSpy = jest.spyOn(beacon, "emit"); + const emitSpy = vi.spyOn(beacon, "emit"); beacon.monitorLiveness(); @@ -356,7 +358,7 @@ describe("Beacon", () => { // and has a 3hr duration const beacon = new Beacon(liveBeaconEvent); expect(beacon.isLive).toBeTruthy(); - const emitSpy = jest.spyOn(beacon, "emit"); + const emitSpy = vi.spyOn(beacon, "emit"); beacon.monitorLiveness(); advanceDateAndTime(HOUR_MS * 2 + 1); @@ -386,7 +388,7 @@ describe("Beacon", () => { // and has a 3hr duration const beacon = new Beacon(liveBeaconEvent); expect(beacon.isLive).toBeTruthy(); - const emitSpy = jest.spyOn(beacon, "emit"); + const emitSpy = vi.spyOn(beacon, "emit"); beacon.monitorLiveness(); @@ -406,7 +408,7 @@ describe("Beacon", () => { describe("addLocations", () => { it("ignores locations when beacon is not live", () => { const beacon = new Beacon(makeBeaconInfoEvent(userId, roomId, { isLive: false })); - const emitSpy = jest.spyOn(beacon, "emit"); + const emitSpy = vi.spyOn(beacon, "emit"); beacon.addLocations([ makeBeaconEvent(userId, { beaconInfoId: beacon.beaconInfoId, timestamp: now + 1 }), @@ -418,7 +420,7 @@ describe("Beacon", () => { it("ignores locations outside the beacon live duration", () => { const beacon = new Beacon(makeBeaconInfoEvent(userId, roomId, { isLive: true, timeout: 60000 })); - const emitSpy = jest.spyOn(beacon, "emit"); + const emitSpy = vi.spyOn(beacon, "emit"); beacon.addLocations([ // beacon has now + 60000 live period @@ -431,7 +433,7 @@ describe("Beacon", () => { it("should ignore invalid beacon events", () => { const beacon = new Beacon(makeBeaconInfoEvent(userId, roomId, { isLive: true, timeout: 60000 })); - const emitSpy = jest.spyOn(beacon, "emit"); + const emitSpy = vi.spyOn(beacon, "emit"); const ev = new MatrixEvent({ type: M_BEACON_INFO.name, @@ -460,7 +462,7 @@ describe("Beacon", () => { timestamp: startTimestamp, }), ); - const emitSpy = jest.spyOn(beacon, "emit"); + const emitSpy = vi.spyOn(beacon, "emit"); beacon.addLocations([ // beacon has now + 60000 live period @@ -483,7 +485,7 @@ describe("Beacon", () => { timestamp: startTimestamp, }), ); - const emitSpy = jest.spyOn(beacon, "emit"); + const emitSpy = vi.spyOn(beacon, "emit"); beacon.addLocations([ // beacon has now + 600000 live period @@ -501,7 +503,7 @@ describe("Beacon", () => { it("sets latest location state to most recent location", () => { const beacon = new Beacon(makeBeaconInfoEvent(userId, roomId, { isLive: true, timeout: 60000 })); - const emitSpy = jest.spyOn(beacon, "emit"); + const emitSpy = vi.spyOn(beacon, "emit"); const locations = [ // older @@ -553,7 +555,7 @@ describe("Beacon", () => { ); expect(beacon.latestLocationEvent).toEqual(newerLocation); - const emitSpy = jest.spyOn(beacon, "emit").mockClear(); + const emitSpy = vi.spyOn(beacon, "emit").mockClear(); // add older location beacon.addLocations([olderLocation]); diff --git a/spec/unit/models/event.spec.ts b/spec/unit/models/event.spec.ts index 0234eab3e09..c391572d9bc 100644 --- a/spec/unit/models/event.spec.ts +++ b/spec/unit/models/event.spec.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { MockedObject } from "jest-mock"; +import { beforeEach, describe, expect, it, MockedObject, vi } from "vitest"; import { MatrixEvent, MatrixEventEvent } from "../../../src/models/event"; import { emitPromise } from "../../test-utils/test-utils"; @@ -249,9 +249,9 @@ describe("MatrixEvent", () => { function createMockClient(): MatrixClient { return { - supportsThreads: jest.fn().mockReturnValue(true), - decryptEventIfNeeded: jest.fn().mockReturnThis(), - getUserId: jest.fn().mockReturnValue("@user:server"), + supportsThreads: vi.fn().mockReturnValue(true), + decryptEventIfNeeded: vi.fn().mockReturnThis(), + getUserId: vi.fn().mockReturnValue("@user:server"), } as unknown as MockedObject; } @@ -363,12 +363,12 @@ describe("MatrixEvent", () => { }); it("should report unknown decryption errors", async () => { - const decryptionListener = jest.fn(); + const decryptionListener = vi.fn(); encryptedEvent.addListener(MatrixEventEvent.Decrypted, decryptionListener); const testError = new Error("test error"); const crypto = { - decryptEvent: jest.fn().mockRejectedValue(testError), + decryptEvent: vi.fn().mockRejectedValue(testError), } as unknown as Crypto; await encryptedEvent.attemptDecryption(crypto); @@ -385,12 +385,12 @@ describe("MatrixEvent", () => { }); it("should report known decryption errors", async () => { - const decryptionListener = jest.fn(); + const decryptionListener = vi.fn(); encryptedEvent.addListener(MatrixEventEvent.Decrypted, decryptionListener); const testError = new DecryptionError(DecryptionFailureCode.MEGOLM_UNKNOWN_INBOUND_SESSION_ID, "uisi"); const crypto = { - decryptEvent: jest.fn().mockRejectedValue(testError), + decryptEvent: vi.fn().mockRejectedValue(testError), } as unknown as Crypto; await encryptedEvent.attemptDecryption(crypto); @@ -410,7 +410,7 @@ describe("MatrixEvent", () => { it(`should report "DecryptionError: The sender has disabled encrypting to unverified devices."`, async () => { const crypto = { - decryptEvent: jest + decryptEvent: vi .fn() .mockRejectedValue( new DecryptionError( @@ -432,10 +432,10 @@ describe("MatrixEvent", () => { }); it("should retry decryption if a retry is queued", async () => { - const eventAttemptDecryptionSpy = jest.spyOn(encryptedEvent, "attemptDecryption"); + const eventAttemptDecryptionSpy = vi.spyOn(encryptedEvent, "attemptDecryption"); const crypto = { - decryptEvent: jest + decryptEvent: vi .fn() .mockImplementationOnce(() => { // schedule a second decryption attempt while @@ -477,7 +477,7 @@ describe("MatrixEvent", () => { }); const crypto = { - decryptEvent: jest.fn().mockImplementationOnce(() => { + decryptEvent: vi.fn().mockImplementationOnce(() => { return Promise.resolve({ clearEvent: { type: "m.room.message", diff --git a/spec/unit/models/poll.spec.ts b/spec/unit/models/poll.spec.ts index 401e338096d..25b146c8a80 100644 --- a/spec/unit/models/poll.spec.ts +++ b/spec/unit/models/poll.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { beforeEach, describe, expect, it, vi } from "vitest"; + import { M_POLL_START } from "matrix-events-sdk"; import { EventType, IEvent, MatrixEvent, PollEvent, Room } from "../../../src"; @@ -25,18 +27,18 @@ import { getMockClientWithEventEmitter, mockClientMethodsUser } from "../../test import { flushPromises } from "../../test-utils/flushPromises"; import { mkEvent } from "../../test-utils/test-utils"; -jest.useFakeTimers(); +vi.useFakeTimers(); describe("Poll", () => { const userId = "@alice:server.org"; const mockClient = getMockClientWithEventEmitter({ ...mockClientMethodsUser(userId), - decryptEventIfNeeded: jest.fn().mockResolvedValue(true), - relations: jest.fn(), + decryptEventIfNeeded: vi.fn().mockResolvedValue(true), + relations: vi.fn(), }); const roomId = "!room:server"; const room = new Room(roomId, mockClient, userId); - const maySendRedactionForEventSpy = jest.spyOn(room.currentState, "maySendRedactionForEvent"); + const maySendRedactionForEventSpy = vi.spyOn(room.currentState, "maySendRedactionForEvent"); // 14.03.2022 16:15 const now = 1647270879403; @@ -48,8 +50,8 @@ describe("Poll", () => { basePollStartEvent.event.event_id = "$12345"; beforeEach(() => { - jest.clearAllMocks(); - jest.setSystemTime(now); + vi.clearAllMocks(); + vi.setSystemTime(now); mockClient.relations.mockReset().mockResolvedValue({ events: [] }); @@ -100,7 +102,7 @@ describe("Poll", () => { describe("fetching responses", () => { it("calls relations api and emits", async () => { const poll = new Poll(basePollStartEvent, mockClient, room); - const emitSpy = jest.spyOn(poll, "emit"); + const emitSpy = vi.spyOn(poll, "emit"); const fetchResponsePromise = poll.getResponses(); expect(poll.isFetchingResponses).toBe(true); const responses = await fetchResponsePromise; @@ -175,7 +177,7 @@ describe("Poll", () => { }); const poll = new Poll(basePollStartEvent, mockClient, room); - jest.spyOn(poll, "emit"); + vi.spyOn(poll, "emit"); const responses = await poll.getResponses(); await flushPromises(); @@ -197,13 +199,13 @@ describe("Poll", () => { const replyEvent = makeRelatedEvent({ type: "m.room.message" }); const stableResponseEvent = makeRelatedEvent({ type: M_POLL_RESPONSE.stable! }); const undecryptableEvent = makeRelatedEvent({ type: M_POLL_RESPONSE.unstable }); - jest.spyOn(undecryptableEvent, "isDecryptionFailure").mockReturnValue(true); + vi.spyOn(undecryptableEvent, "isDecryptionFailure").mockReturnValue(true); mockClient.relations.mockResolvedValue({ events: [replyEvent, stableResponseEvent, undecryptableEvent], }); const poll = new Poll(basePollStartEvent, mockClient, room); - jest.spyOn(poll, "emit"); + vi.spyOn(poll, "emit"); await poll.getResponses(); expect(poll.undecryptableRelationsCount).toBe(1); expect(poll.emit).toHaveBeenCalledWith(PollEvent.UndecryptableRelations, 1); @@ -214,14 +216,14 @@ describe("Poll", () => { const stableResponseEvent = makeRelatedEvent({ type: M_POLL_RESPONSE.stable! }); const undecryptableEvent = makeRelatedEvent({ type: M_POLL_RESPONSE.unstable }); const undecryptableEvent2 = makeRelatedEvent({ type: M_POLL_RESPONSE.unstable }); - jest.spyOn(undecryptableEvent, "isDecryptionFailure").mockReturnValue(true); - jest.spyOn(undecryptableEvent2, "isDecryptionFailure").mockReturnValue(true); + vi.spyOn(undecryptableEvent, "isDecryptionFailure").mockReturnValue(true); + vi.spyOn(undecryptableEvent2, "isDecryptionFailure").mockReturnValue(true); mockClient.relations.mockResolvedValue({ events: [replyEvent, stableResponseEvent, undecryptableEvent], }); const poll = new Poll(basePollStartEvent, mockClient, room); - jest.spyOn(poll, "emit"); + vi.spyOn(poll, "emit"); await poll.getResponses(); expect(poll.undecryptableRelationsCount).toBe(1); @@ -248,7 +250,7 @@ describe("Poll", () => { it("sets poll end event with stable event type", async () => { const poll = new Poll(basePollStartEvent, mockClient, room); - jest.spyOn(poll, "emit"); + vi.spyOn(poll, "emit"); await poll.getResponses(); expect(maySendRedactionForEventSpy).toHaveBeenCalledWith(basePollStartEvent, "@bob@server.org"); @@ -270,7 +272,7 @@ describe("Poll", () => { events: [pollEndEvent], }); maySendRedactionForEventSpy.mockReturnValue(false); - jest.spyOn(poll, "emit"); + vi.spyOn(poll, "emit"); await poll.getResponses(); expect(maySendRedactionForEventSpy).not.toHaveBeenCalled(); @@ -283,7 +285,7 @@ describe("Poll", () => { events: [unstablePollEndEvent], }); const poll = new Poll(basePollStartEvent, mockClient, room); - jest.spyOn(poll, "emit"); + vi.spyOn(poll, "emit"); await poll.getResponses(); expect(poll.isEnded).toBe(true); @@ -304,7 +306,7 @@ describe("Poll", () => { describe("onNewRelation()", () => { it("discards response if poll responses have not been initialised", () => { const poll = new Poll(basePollStartEvent, mockClient, room); - jest.spyOn(poll, "emit"); + vi.spyOn(poll, "emit"); const responseEvent = makeRelatedEvent({ type: M_POLL_RESPONSE.name }, now); poll.onNewRelation(responseEvent); @@ -315,7 +317,7 @@ describe("Poll", () => { it("sets poll end event when responses are not initialised", () => { const poll = new Poll(basePollStartEvent, mockClient, room); - jest.spyOn(poll, "emit"); + vi.spyOn(poll, "emit"); const stablePollEndEvent = makeRelatedEvent({ type: M_POLL_END.stable!, sender: userId }); poll.onNewRelation(stablePollEndEvent); @@ -332,7 +334,7 @@ describe("Poll", () => { }); const poll = new Poll(basePollStartEvent, mockClient, room); await poll.getResponses(); - jest.spyOn(poll, "emit"); + vi.spyOn(poll, "emit"); poll.onNewRelation(stablePollEndEvent); @@ -367,7 +369,7 @@ describe("Poll", () => { expect(poll.isEnded).toBeTruthy(); // reset spy count - jest.spyOn(poll, "emit").mockClear(); + vi.spyOn(poll, "emit").mockClear(); // add a valid end event with earlier timestamp poll.onNewRelation(earlierPollEndEvent); @@ -398,7 +400,7 @@ describe("Poll", () => { expect(poll.isEnded).toBeTruthy(); // reset spy count - jest.spyOn(poll, "emit").mockClear(); + vi.spyOn(poll, "emit").mockClear(); poll.onNewRelation(laterPollEndEvent); // didn't set new end event, didn't refilter responses @@ -416,7 +418,7 @@ describe("Poll", () => { }); const poll = new Poll(basePollStartEvent, mockClient, room); const responses = await poll.getResponses(); - jest.spyOn(poll, "emit"); + vi.spyOn(poll, "emit"); expect(responses.getRelations().length).toEqual(3); poll.onNewRelation(stablePollEndEvent); @@ -432,7 +434,7 @@ describe("Poll", () => { const poll = new Poll(basePollStartEvent, mockClient, room); // init responses const responses = await poll.getResponses(); - jest.spyOn(poll, "emit"); + vi.spyOn(poll, "emit"); const replyEvent = new MatrixEvent({ type: "m.room.message" }); poll.onNewRelation(replyEvent); @@ -446,7 +448,7 @@ describe("Poll", () => { const poll = new Poll(basePollStartEvent, mockClient, room); // init responses const responses = await poll.getResponses(); - jest.spyOn(poll, "emit"); + vi.spyOn(poll, "emit"); const responseEvent = makeRelatedEvent({ type: M_POLL_RESPONSE.name }, now); poll.onNewRelation(responseEvent); diff --git a/spec/unit/models/room-receipts.spec.ts b/spec/unit/models/room-receipts.spec.ts index b352c0defd7..6a81ae176a4 100644 --- a/spec/unit/models/room-receipts.spec.ts +++ b/spec/unit/models/room-receipts.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; + import { FeatureSupport, MatrixClient, MatrixEvent, ReceiptContent, THREAD_RELATION_TYPE, Thread } from "../../../src"; import { Room } from "../../../src/models/room"; @@ -29,7 +31,7 @@ describe("RoomReceipts", () => { }); afterAll(() => { - jest.restoreAllMocks(); + vi.restoreAllMocks(); }); it("reports events unread if there are no receipts", () => { @@ -429,13 +431,13 @@ describe("RoomReceipts", () => { function createFakeClient(): MatrixClient { return { - getUserId: jest.fn(), - getEventMapper: jest.fn().mockReturnValue(jest.fn()), - isInitialSyncComplete: jest.fn().mockReturnValue(true), - supportsThreads: jest.fn().mockReturnValue(true), - fetchRoomEvent: jest.fn().mockResolvedValue({}), - paginateEventTimeline: jest.fn(), - canSupport: { get: jest.fn() }, + getUserId: vi.fn(), + getEventMapper: vi.fn().mockReturnValue(vi.fn()), + isInitialSyncComplete: vi.fn().mockReturnValue(true), + supportsThreads: vi.fn().mockReturnValue(true), + fetchRoomEvent: vi.fn().mockResolvedValue({}), + paginateEventTimeline: vi.fn(), + canSupport: { get: vi.fn() }, } as unknown as MatrixClient; } diff --git a/spec/unit/models/thread.spec.ts b/spec/unit/models/thread.spec.ts index f7dab002a00..c55abf243d3 100644 --- a/spec/unit/models/thread.spec.ts +++ b/spec/unit/models/thread.spec.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { mocked } from "jest-mock"; +import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { MatrixClient, PendingEventOrdering } from "../../../src/client"; import { Room, RoomEvent } from "../../../src/models/room"; @@ -28,6 +28,7 @@ import { getMockClientWithEventEmitter, mockClientMethodsUser } from "../../test import { ReEmitter } from "../../../src/ReEmitter"; import { Feature, ServerSupport } from "../../../src/feature"; import { eventMapperFor } from "../../../src/event-mapper"; +import { mocked } from "../../test-utils"; describe("Thread", () => { describe("constructor", () => { @@ -43,12 +44,12 @@ describe("Thread", () => { const myUserId = "@bob:example.org"; const testClient = new TestClient(myUserId, "DEVICE", "ACCESS_TOKEN", undefined, { timelineSupport: false }); const client = testClient.client; - client.supportsThreads = jest.fn().mockReturnValue(true); + client.supportsThreads = vi.fn().mockReturnValue(true); const room = new Room("123", client, myUserId, { pendingEventOrdering: PendingEventOrdering.Detached, }); - jest.spyOn(client, "getRoom").mockReturnValue(room); + vi.spyOn(client, "getRoom").mockReturnValue(room); const { thread } = mkThread({ room, @@ -86,10 +87,10 @@ describe("Thread", () => { beforeEach(() => { client = getMockClientWithEventEmitter({ ...mockClientMethodsUser(), - isInitialSyncComplete: jest.fn().mockReturnValue(false), - getRoom: jest.fn().mockImplementation(() => room), - decryptEventIfNeeded: jest.fn().mockResolvedValue(void 0), - supportsThreads: jest.fn().mockReturnValue(true), + isInitialSyncComplete: vi.fn().mockReturnValue(false), + getRoom: vi.fn().mockImplementation(() => room), + decryptEventIfNeeded: vi.fn().mockResolvedValue(void 0), + supportsThreads: vi.fn().mockReturnValue(true), }); client.reEmitter = mock(ReEmitter, "ReEmitter"); client.canSupport = new Map(); @@ -128,11 +129,11 @@ describe("Thread", () => { }); room.addReceipt(receipt); - jest.spyOn(client, "getRoom").mockReturnValue(room); + vi.spyOn(client, "getRoom").mockReturnValue(room); }); afterAll(() => { - jest.resetAllMocks(); + vi.resetAllMocks(); }); it("considers own events with no RR as read", () => { @@ -216,10 +217,10 @@ describe("Thread", () => { beforeEach(() => { client = getMockClientWithEventEmitter({ ...mockClientMethodsUser(), - isInitialSyncComplete: jest.fn().mockReturnValue(false), - getRoom: jest.fn().mockImplementation(() => room), - decryptEventIfNeeded: jest.fn().mockResolvedValue(void 0), - supportsThreads: jest.fn().mockReturnValue(true), + isInitialSyncComplete: vi.fn().mockReturnValue(false), + getRoom: vi.fn().mockImplementation(() => room), + decryptEventIfNeeded: vi.fn().mockResolvedValue(void 0), + supportsThreads: vi.fn().mockReturnValue(true), }); client.reEmitter = mock(ReEmitter, "ReEmitter"); client.canSupport = new Map(); @@ -231,11 +232,11 @@ describe("Thread", () => { room = new Room("123", client, myUserId); - jest.spyOn(client, "getRoom").mockReturnValue(room); + vi.spyOn(client, "getRoom").mockReturnValue(room); }); afterAll(() => { - jest.resetAllMocks(); + vi.resetAllMocks(); }); it("uses unthreaded receipt to figure out read up to", () => { @@ -319,12 +320,12 @@ describe("Thread", () => { timelineSupport: false, }); const client = testClient.client; - client.supportsThreads = jest.fn().mockReturnValue(true); + client.supportsThreads = vi.fn().mockReturnValue(true); const room = new Room("123", client, myUserId, { pendingEventOrdering: PendingEventOrdering.Detached, }); - jest.spyOn(client, "getRoom").mockReturnValue(room); + vi.spyOn(client, "getRoom").mockReturnValue(room); const { thread } = mkThread({ room, @@ -336,7 +337,7 @@ describe("Thread", () => { await emitPromise(thread, ThreadEvent.Update); expect(thread.length).toBe(2); - jest.spyOn(client, "createMessagesRequest").mockImplementation((_, token) => + vi.spyOn(client, "createMessagesRequest").mockImplementation((_, token) => Promise.resolve({ chunk: [], start: `${token}-new`, @@ -374,12 +375,12 @@ describe("Thread", () => { timelineSupport: false, }); const client = testClient.client; - client.supportsThreads = jest.fn().mockReturnValue(true); + client.supportsThreads = vi.fn().mockReturnValue(true); const room = new Room("123", client, myUserId, { pendingEventOrdering: PendingEventOrdering.Detached, }); - jest.spyOn(client, "getRoom").mockReturnValue(room); + vi.spyOn(client, "getRoom").mockReturnValue(room); const { thread } = mkThread({ room, @@ -391,7 +392,7 @@ describe("Thread", () => { await emitPromise(thread, ThreadEvent.Update); expect(thread.length).toBe(2); - jest.spyOn(client, "createMessagesRequest").mockImplementation((_, token) => + vi.spyOn(client, "createMessagesRequest").mockImplementation((_, token) => Promise.resolve({ chunk: [], start: `${token}-new`, @@ -426,12 +427,12 @@ describe("Thread", () => { timelineSupport: false, }); const client = testClient.client; - client.supportsThreads = jest.fn().mockReturnValue(true); + client.supportsThreads = vi.fn().mockReturnValue(true); const room = new Room("123", client, myUserId, { pendingEventOrdering: PendingEventOrdering.Detached, }); - jest.spyOn(client, "getRoom").mockReturnValue(room); + vi.spyOn(client, "getRoom").mockReturnValue(room); const { thread } = mkThread({ room, @@ -442,7 +443,7 @@ describe("Thread", () => { }); await emitPromise(thread, ThreadEvent.Update); expect(thread.length).toBe(2); - const mock = jest.spyOn(thread, "resetLiveTimeline"); + const mock = vi.spyOn(thread, "resetLiveTimeline"); mock.mockReturnValue(Promise.resolve()); room.resetLiveTimeline("b1", "f1"); @@ -637,7 +638,7 @@ describe("Thread", () => { const client = mock(MatrixClient, "MatrixClient"); client.reEmitter = mock(ReEmitter, "ReEmitter"); client.canSupport = canSupport; - jest.spyOn(client, "getEventMapper").mockReturnValue(eventMapperFor(client, {})); + vi.spyOn(client, "getEventMapper").mockReturnValue(eventMapperFor(client, {})); mocked(client.supportsThreads).mockReturnValue(true); return client; } @@ -819,13 +820,13 @@ function createClient(canSupport = new Map()): MatrixClient { client.reEmitter = mock(ReEmitter, "ReEmitter"); client.canSupport = canSupport; - jest.spyOn(client, "supportsThreads").mockReturnValue(true); - jest.spyOn(client, "getEventMapper").mockReturnValue(eventMapperFor(client, {})); + vi.spyOn(client, "supportsThreads").mockReturnValue(true); + vi.spyOn(client, "getEventMapper").mockReturnValue(eventMapperFor(client, {})); // Mock methods that call out to HTTP endpoints - jest.spyOn(client, "paginateEventTimeline").mockResolvedValue(true); - jest.spyOn(client, "relations").mockResolvedValue({ events: [] }); - jest.spyOn(client, "fetchRoomEvent").mockResolvedValue({}); + vi.spyOn(client, "paginateEventTimeline").mockResolvedValue(true); + vi.spyOn(client, "relations").mockResolvedValue({ events: [] }); + vi.spyOn(client, "fetchRoomEvent").mockResolvedValue({}); return client; } diff --git a/spec/unit/notifications.spec.ts b/spec/unit/notifications.spec.ts index f559e0f2f2c..3220a121692 100644 --- a/spec/unit/notifications.spec.ts +++ b/spec/unit/notifications.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { beforeEach, describe, expect, it, vi } from "vitest"; + import { ReceiptType } from "../../src/@types/read_receipts"; import { Feature, ServerSupport } from "../../src/feature"; import { @@ -53,11 +55,11 @@ describe("fixNotificationCountOnDecryption", () => { beforeEach(() => { mockClient = getMockClientWithEventEmitter({ ...mockClientMethodsUser(), - isInitialSyncComplete: jest.fn().mockReturnValue(false), - getPushActionsForEvent: jest.fn().mockReturnValue(mkPushAction(true, true)), - getRoom: jest.fn().mockImplementation(() => room), - decryptEventIfNeeded: jest.fn().mockResolvedValue(void 0), - supportsThreads: jest.fn().mockReturnValue(true), + isInitialSyncComplete: vi.fn().mockReturnValue(false), + getPushActionsForEvent: vi.fn().mockReturnValue(mkPushAction(true, true)), + getRoom: vi.fn().mockImplementation(() => room), + decryptEventIfNeeded: vi.fn().mockResolvedValue(void 0), + supportsThreads: vi.fn().mockReturnValue(true), }); mockClient.reEmitter = mock(ReEmitter, "ReEmitter"); mockClient.canSupport = new Map(); @@ -127,8 +129,8 @@ describe("fixNotificationCountOnDecryption", () => { room.setThreadUnreadNotificationCount(THREAD_ID, NotificationCountType.Total, 1); room.setThreadUnreadNotificationCount(THREAD_ID, NotificationCountType.Highlight, 0); - event.getPushActions = jest.fn().mockReturnValue(mkPushAction(false, false)); - threadEvent.getPushActions = jest.fn().mockReturnValue(mkPushAction(false, false)); + event.getPushActions = vi.fn().mockReturnValue(mkPushAction(false, false)); + threadEvent.getPushActions = vi.fn().mockReturnValue(mkPushAction(false, false)); }); it("changes the room count to highlight on decryption", () => { @@ -200,8 +202,8 @@ describe("fixNotificationCountOnDecryption", () => { room.setUnreadNotificationCount(NotificationCountType.Total, 0); room.setUnreadNotificationCount(NotificationCountType.Highlight, 0); - event.getPushActions = jest.fn().mockReturnValue(mkPushAction(true, false)); - mockClient.getPushActionsForEvent = jest.fn().mockReturnValue(mkPushAction(false, false)); + event.getPushActions = vi.fn().mockReturnValue(mkPushAction(true, false)); + mockClient.getPushActionsForEvent = vi.fn().mockReturnValue(mkPushAction(false, false)); fixNotificationCountOnDecryption(mockClient, event); expect(room.getUnreadNotificationCount(NotificationCountType.Total)).toBe(0); @@ -212,8 +214,8 @@ describe("fixNotificationCountOnDecryption", () => { room.setThreadUnreadNotificationCount(THREAD_ID, NotificationCountType.Total, 0); room.setThreadUnreadNotificationCount(THREAD_ID, NotificationCountType.Highlight, 0); - threadEvent.getPushActions = jest.fn().mockReturnValue(mkPushAction(true, false)); - mockClient.getPushActionsForEvent = jest.fn().mockReturnValue(mkPushAction(false, false)); + threadEvent.getPushActions = vi.fn().mockReturnValue(mkPushAction(true, false)); + mockClient.getPushActionsForEvent = vi.fn().mockReturnValue(mkPushAction(false, false)); fixNotificationCountOnDecryption(mockClient, event); expect(room.getThreadUnreadNotificationCount(THREAD_ID, NotificationCountType.Total)).toBe(0); @@ -221,7 +223,7 @@ describe("fixNotificationCountOnDecryption", () => { }); it("emits events", () => { - const cb = jest.fn(); + const cb = vi.fn(); room.on(RoomEvent.UnreadNotifications, cb); room.setUnreadNotificationCount(NotificationCountType.Total, 1); diff --git a/spec/unit/oidc/authorize.spec.ts b/spec/unit/oidc/authorize.spec.ts index 2c3657bea42..a812e18c82b 100644 --- a/spec/unit/oidc/authorize.spec.ts +++ b/spec/unit/oidc/authorize.spec.ts @@ -1,5 +1,5 @@ /** - * @jest-environment jsdom + * @vitest-environment jsdom */ /* @@ -18,10 +18,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -import fetchMock from "fetch-mock-jest"; -import { mocked } from "jest-mock"; +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; + +import fetchMock from "@fetch-mock/vitest"; import { jwtDecode } from "jwt-decode"; -import { Crypto } from "@peculiar/webcrypto"; import { getRandomValues } from "node:crypto"; import { TextEncoder } from "node:util"; @@ -35,8 +35,9 @@ import { } from "../../../src/oidc/authorize"; import { OidcError } from "../../../src/oidc/error"; import { makeDelegatedAuthConfig, mockOpenIdConfiguration } from "../../test-utils/oidc"; +import { mocked } from "../../test-utils"; -jest.mock("jwt-decode"); +vi.mock("jwt-decode"); describe("oidc authorization", () => { const delegatedAuthConfig = makeDelegatedAuthConfig(); @@ -49,8 +50,8 @@ describe("oidc authorization", () => { const now = 1647270879403; beforeAll(() => { - jest.spyOn(logger, "warn"); - jest.setSystemTime(now); + vi.spyOn(logger, "warn"); + vi.setSystemTime(now); fetchMock.get(delegatedAuthConfig.issuer + ".well-known/openid-configuration", mockOpenIdConfiguration()); globalThis.TextEncoder = TextEncoder; @@ -61,7 +62,7 @@ describe("oidc authorization", () => { Object.defineProperty(window, "crypto", { value: { getRandomValues, - randomUUID: jest.fn().mockReturnValue("not-random-uuid"), + randomUUID: vi.fn().mockReturnValue("not-random-uuid"), subtle: webCrypto.subtle, }, }); @@ -196,14 +197,14 @@ describe("oidc authorization", () => { }; const mockSessionStorage = (state: Record): void => { - jest.spyOn(sessionStorage.__proto__, "getItem").mockImplementation((key: unknown) => { + vi.spyOn(sessionStorage.__proto__, "getItem").mockImplementation((key: unknown) => { return state[key as string] ?? null; }); - jest.spyOn(sessionStorage.__proto__, "setItem").mockImplementation( + vi.spyOn(sessionStorage.__proto__, "setItem").mockImplementation( // @ts-ignore mock type (key: string, value: unknown) => (state[key] = value), ); - jest.spyOn(sessionStorage.__proto__, "removeItem").mockImplementation((key: unknown) => { + vi.spyOn(sessionStorage.__proto__, "removeItem").mockImplementation((key: unknown) => { const { [key as string]: value, ...newState } = state; state = newState; return value; @@ -251,7 +252,7 @@ describe("oidc authorization", () => { beforeEach(() => { fetchMock.mockClear(); - fetchMock.resetBehavior(); + fetchMock.mockReset(); fetchMock.get(`${metadata.issuer}.well-known/openid-configuration`, metadata); fetchMock.get(`${metadata.issuer}jwks`, { @@ -285,8 +286,8 @@ describe("oidc authorization", () => { ); // check body is correctly formed - const queryParams = fetchMock.mock.calls.find(([endpoint]) => endpoint === metadata.token_endpoint)![1]! - .body as URLSearchParams; + const queryParams = fetchMock.callHistory.lastCall((callLog) => callLog.url === metadata.token_endpoint)! + .request!.body as URLSearchParams; expect(queryParams.get("grant_type")).toEqual("authorization_code"); expect(queryParams.get("client_id")).toEqual(clientId); expect(queryParams.get("code_verifier")).toEqual(codeVerifier); @@ -327,16 +328,12 @@ describe("oidc authorization", () => { scope, token_type: "bearer", }; - fetchMock.post( - tokenEndpoint, - { - headers: { - "Content-Type": "application/json", - }, - ...tokenResponse, + fetchMock.post(tokenEndpoint, { + headers: { + "Content-Type": "application/json", }, - { overwriteRoutes: true }, - ); + ...tokenResponse, + }); const result = await completeAuthorizationCodeGrant(code, state); @@ -366,13 +363,9 @@ describe("oidc authorization", () => { it("should throw when state is not found in storage", async () => { // don't setup sessionStorage with expected state const state = "abc123"; - fetchMock.post( - metadata.token_endpoint, - { - status: 500, - }, - { overwriteRoutes: true }, - ); + fetchMock.post(metadata.token_endpoint, { + status: 500, + }); await expect(() => completeAuthorizationCodeGrant(code, state)).rejects.toThrow( new Error(OidcError.MissingOrInvalidStoredState), ); @@ -380,13 +373,9 @@ describe("oidc authorization", () => { it("should throw with code exchange failed error when request fails", async () => { const state = await setupState(); - fetchMock.post( - metadata.token_endpoint, - { - status: 500, - }, - { overwriteRoutes: true }, - ); + fetchMock.post(metadata.token_endpoint, { + status: 500, + }); await expect(() => completeAuthorizationCodeGrant(code, state)).rejects.toThrow( new Error(OidcError.CodeExchangeFailed), ); @@ -398,16 +387,12 @@ describe("oidc authorization", () => { ...validBearerTokenResponse, access_token: null, }; - fetchMock.post( - metadata.token_endpoint, - { - headers: { - "Content-Type": "application/json", - }, - ...invalidBearerTokenResponse, + fetchMock.post(metadata.token_endpoint, { + headers: { + "Content-Type": "application/json", }, - { overwriteRoutes: true }, - ); + ...invalidBearerTokenResponse, + }); await expect(() => completeAuthorizationCodeGrant(code, state)).rejects.toThrow( new Error(OidcError.InvalidBearerTokenResponse), ); diff --git a/spec/unit/oidc/register.spec.ts b/spec/unit/oidc/register.spec.ts index 0f02b3fd28c..bb867ffe99f 100644 --- a/spec/unit/oidc/register.spec.ts +++ b/spec/unit/oidc/register.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { beforeEach, describe, expect, it } from "vitest"; + import fetchMockJest from "fetch-mock-jest"; import { OidcError } from "../../../src/oidc/error"; diff --git a/spec/unit/oidc/tokenRefresher.spec.ts b/spec/unit/oidc/tokenRefresher.spec.ts index 43a236afb8d..05f7996867c 100644 --- a/spec/unit/oidc/tokenRefresher.spec.ts +++ b/spec/unit/oidc/tokenRefresher.spec.ts @@ -1,5 +1,5 @@ /** - * @jest-environment jsdom + * @vitest-environment jsdom */ /* @@ -18,7 +18,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -import fetchMock from "fetch-mock-jest"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + +import fetchMock from "@fetch-mock/vitest"; import { OidcTokenRefresher } from "../../../src"; import { logger } from "../../../src/logger"; @@ -74,20 +76,16 @@ describe("OidcTokenRefresher", () => { }); afterEach(() => { - jest.restoreAllMocks(); - fetchMock.resetBehavior(); + vi.restoreAllMocks(); + fetchMock.mockReset(); }); it("throws when oidc client cannot be initialised", async () => { - jest.spyOn(logger, "error"); - fetchMock.get( - `${config.issuer}.well-known/openid-configuration`, - { - ok: false, - status: 404, - }, - { overwriteRoutes: true }, - ); + vi.spyOn(logger, "error"); + fetchMock.get(`${config.issuer}.well-known/openid-configuration`, { + ok: false, + status: 404, + }); const refresher = new OidcTokenRefresher(authConfig.issuer, clientId, redirectUri, deviceId, idTokenClaims); await expect(refresher.oidcClientReady).rejects.toThrow(); expect(logger.error).toHaveBeenCalledWith( @@ -140,7 +138,7 @@ describe("OidcTokenRefresher", () => { const refresher = new OidcTokenRefresher(authConfig.issuer, clientId, redirectUri, deviceId, idTokenClaims); await refresher.oidcClientReady; // spy on our stub - jest.spyOn(refresher, "persistTokens"); + vi.spyOn(refresher, "persistTokens"); await refresher.doRefreshAccessToken("refresh-token"); @@ -152,33 +150,25 @@ describe("OidcTokenRefresher", () => { it("should only have one inflight refresh request at once", async () => { fetchMock - .postOnce( - config.token_endpoint, - { - status: 200, - headers: { - "Content-Type": "application/json", - }, - ...makeTokenResponse("first-new-access-token", "first-new-refresh-token"), + .postOnce(config.token_endpoint, { + status: 200, + headers: { + "Content-Type": "application/json", }, - { overwriteRoutes: true }, - ) - .postOnce( - config.token_endpoint, - { - status: 200, - headers: { - "Content-Type": "application/json", - }, - ...makeTokenResponse("second-new-access-token", "second-new-refresh-token"), + ...makeTokenResponse("first-new-access-token", "first-new-refresh-token"), + }) + .postOnce(config.token_endpoint, { + status: 200, + headers: { + "Content-Type": "application/json", }, - { overwriteRoutes: false }, - ); + ...makeTokenResponse("second-new-access-token", "second-new-refresh-token"), + }); const refresher = new OidcTokenRefresher(authConfig.issuer, clientId, redirectUri, deviceId, idTokenClaims); await refresher.oidcClientReady; // reset call counts - fetchMock.resetHistory(); + fetchMock.clearHistory(); const refreshToken = "refresh-token"; const first = refresher.doRefreshAccessToken(refreshToken); @@ -207,16 +197,12 @@ describe("OidcTokenRefresher", () => { }); it("should log and rethrow when token refresh fails", async () => { - fetchMock.post( - config.token_endpoint, - { - status: 503, - headers: { - "Content-Type": "application/json", - }, + fetchMock.post(config.token_endpoint, { + status: 503, + headers: { + "Content-Type": "application/json", }, - { overwriteRoutes: true }, - ); + }); const refresher = new OidcTokenRefresher(authConfig.issuer, clientId, redirectUri, deviceId, idTokenClaims); await refresher.oidcClientReady; @@ -227,32 +213,24 @@ describe("OidcTokenRefresher", () => { it("should make fresh request after a failed request", async () => { // make sure inflight request is cleared after a failure fetchMock - .postOnce( - config.token_endpoint, - { - status: 503, - headers: { - "Content-Type": "application/json", - }, + .postOnce(config.token_endpoint, { + status: 503, + headers: { + "Content-Type": "application/json", }, - { overwriteRoutes: true }, - ) - .postOnce( - config.token_endpoint, - { - status: 200, - headers: { - "Content-Type": "application/json", - }, - ...makeTokenResponse("second-new-access-token", "second-new-refresh-token"), + }) + .postOnce(config.token_endpoint, { + status: 200, + headers: { + "Content-Type": "application/json", }, - { overwriteRoutes: false }, - ); + ...makeTokenResponse("second-new-access-token", "second-new-refresh-token"), + }); const refresher = new OidcTokenRefresher(authConfig.issuer, clientId, redirectUri, deviceId, idTokenClaims); await refresher.oidcClientReady; // reset call counts - fetchMock.resetHistory(); + fetchMock.clearHistory(); // first call fails await expect(refresher.doRefreshAccessToken("refresh-token")).rejects.toThrow(); diff --git a/spec/unit/oidc/validate.spec.ts b/spec/unit/oidc/validate.spec.ts index 4802e22da7f..6a2f632d023 100644 --- a/spec/unit/oidc/validate.spec.ts +++ b/spec/unit/oidc/validate.spec.ts @@ -14,14 +14,16 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { mocked } from "jest-mock"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + import { jwtDecode } from "jwt-decode"; import { logger } from "../../../src/logger"; import { ValidatedAuthMetadata, validateIdToken, validateAuthMetadata } from "../../../src/oidc/validate"; import { OidcError } from "../../../src/oidc/error"; +import { mocked } from "../../test-utils"; -jest.mock("jwt-decode"); +vi.mock("jwt-decode"); describe("validateOIDCIssuerWellKnown", () => { const validWk: ValidatedAuthMetadata = { @@ -38,7 +40,7 @@ describe("validateOIDCIssuerWellKnown", () => { }; beforeEach(() => { // stub to avoid console litter - jest.spyOn(logger, "error") + vi.spyOn(logger, "error") .mockClear() .mockImplementation(() => {}); }); @@ -137,7 +139,7 @@ describe("validateIdToken()", () => { beforeEach(() => { mocked(jwtDecode).mockClear().mockReturnValue(validDecodedIdToken); - jest.spyOn(logger, "error").mockClear(); + vi.spyOn(logger, "error").mockClear(); }); it("should throw when idToken is falsy", () => { diff --git a/spec/unit/pusher.spec.ts b/spec/unit/pusher.spec.ts index 75d08a270f6..539ffff623a 100644 --- a/spec/unit/pusher.spec.ts +++ b/spec/unit/pusher.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { beforeEach, describe, expect, it } from "vitest"; + import MockHttpBackend from "matrix-mock-request"; import { MatrixClient, PUSHER_ENABLED } from "../../src/matrix"; diff --git a/spec/unit/pushprocessor.spec.ts b/spec/unit/pushprocessor.spec.ts index ed7261e727d..115c2271193 100644 --- a/spec/unit/pushprocessor.spec.ts +++ b/spec/unit/pushprocessor.spec.ts @@ -1,3 +1,4 @@ +import { beforeEach, describe, expect, it } from "vitest"; import * as utils from "../test-utils/test-utils"; import { IActionsObject, PushProcessor } from "../../src/pushprocessor"; import { diff --git a/spec/unit/queueToDevice.spec.ts b/spec/unit/queueToDevice.spec.ts index e4716755cc1..f55762dc35b 100644 --- a/spec/unit/queueToDevice.spec.ts +++ b/spec/unit/queueToDevice.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; + import MockHttpBackend from "matrix-mock-request"; import { indexedDB as fakeIndexedDB } from "fake-indexeddb"; @@ -53,7 +55,7 @@ async function flushAndRunTimersUntil(cond: () => boolean) { while (!cond()) { await flushPromises(); if (cond()) break; - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); } } @@ -182,8 +184,8 @@ describe.each([[StoreType.Memory], [StoreType.IndexedDB]])("queueToDevice (%s st event_id: "!fake:example.org", }); const mockRoom = { - updatePendingEvent: jest.fn(), - hasEncryptionStateEvent: jest.fn().mockReturnValue(false), + updatePendingEvent: vi.fn(), + hasEncryptionStateEvent: vi.fn().mockReturnValue(false), } as unknown as Room; client.resendEvent(dummyEvent, mockRoom); @@ -230,11 +232,11 @@ describe.each([[StoreType.Memory], [StoreType.IndexedDB]])("queueToDevice (%s st describe("async tests", () => { beforeAll(() => { - jest.useFakeTimers(); + vi.useFakeTimers(); }); afterAll(() => { - jest.useRealTimers(); + vi.useRealTimers(); }); beforeEach(async function () { @@ -337,14 +339,14 @@ describe.each([[StoreType.Memory], [StoreType.IndexedDB]])("queueToDevice (%s st logger.info("Advancing clock to just before expected retry time..."); - jest.advanceTimersByTime(retryDelay - 1000); + vi.advanceTimersByTime(retryDelay - 1000); await flushPromises(); expect(httpBackend.requests.length).toEqual(0); logger.info("Advancing clock past expected retry time..."); - jest.advanceTimersByTime(2000); + vi.advanceTimersByTime(2000); await flushPromises(); expect(httpBackend.flushSync(undefined, 1)).toEqual(1); diff --git a/spec/unit/randomstring.spec.ts b/spec/unit/randomstring.spec.ts index 2413ca9588a..2c0550ffec2 100644 --- a/spec/unit/randomstring.spec.ts +++ b/spec/unit/randomstring.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { afterEach, describe, expect, it, vi } from "vitest"; + import { decodeBase64 } from "../../src/base64"; import { secureRandomString, @@ -25,7 +27,7 @@ import { describe("Random strings", () => { afterEach(() => { - jest.restoreAllMocks(); + vi.restoreAllMocks(); }); it.each([8, 16, 32])("secureRandomBase64 generates %i valid base64 bytes", (n: number) => { @@ -102,7 +104,7 @@ describe("Random strings", () => { // mock once to fill with 255 the first time: 255 should be unusable because // we give 10 possible characters below and 256 is not evenly divisible by 10, so // this should force it to call for more entropy. - jest.spyOn(globalThis.crypto, "getRandomValues").mockImplementationOnce((arr) => { + vi.spyOn(globalThis.crypto, "getRandomValues").mockImplementationOnce((arr) => { if (arr === null) throw new Error("Buffer is null"); new Uint8Array(arr.buffer).fill(255); return arr; diff --git a/spec/unit/read-receipt.spec.ts b/spec/unit/read-receipt.spec.ts index 017a0aa3f96..f9f4bf8ec9a 100644 --- a/spec/unit/read-receipt.spec.ts +++ b/spec/unit/read-receipt.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { beforeEach, describe, expect, it, vi } from "vitest"; + import MockHttpBackend from "matrix-mock-request"; import { MAIN_ROOM_TIMELINE, ReceiptType, WrappedReceipt } from "../../src/@types/read_receipts"; @@ -240,7 +242,7 @@ describe("Read receipt", () => { it("should not allow an older unthreaded receipt to clobber a `main` threaded one", () => { const userId = client.getSafeUserId(); const room = new Room(ROOM_ID, client, userId); - room.findEventById = jest.fn().mockReturnValue({} as MatrixEvent); + room.findEventById = vi.fn().mockReturnValue({} as MatrixEvent); const unthreadedReceipt: WrappedReceipt = { eventId: "$olderEvent", diff --git a/spec/unit/realtime-callbacks.spec.ts b/spec/unit/realtime-callbacks.spec.ts index 7f9b83a8bca..2e96898d116 100644 --- a/spec/unit/realtime-callbacks.spec.ts +++ b/spec/unit/realtime-callbacks.spec.ts @@ -14,20 +14,22 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { describe, expect, it, vi } from "vitest"; + import * as callbacks from "../../src/realtime-callbacks"; let wallTime = 1234567890; -jest.useFakeTimers().setSystemTime(wallTime); +vi.useFakeTimers().setSystemTime(wallTime); describe("realtime-callbacks", function () { function tick(millis: number): void { wallTime += millis; - jest.advanceTimersByTime(millis); + vi.advanceTimersByTime(millis); } describe("setTimeout", function () { it("should call the callback after the timeout", function () { - const callback = jest.fn(); + const callback = vi.fn(); callbacks.setTimeout(callback, 100); expect(callback).not.toHaveBeenCalled(); @@ -36,7 +38,7 @@ describe("realtime-callbacks", function () { }); it("should default to a zero timeout", function () { - const callback = jest.fn(); + const callback = vi.fn(); callbacks.setTimeout(callback, 0); expect(callback).not.toHaveBeenCalled(); @@ -45,7 +47,7 @@ describe("realtime-callbacks", function () { }); it("should pass any parameters to the callback", function () { - const callback = jest.fn(); + const callback = vi.fn(); callbacks.setTimeout(callback, 0, "a", "b", "c"); tick(0); expect(callback).toHaveBeenCalledWith("a", "b", "c"); @@ -64,7 +66,7 @@ describe("realtime-callbacks", function () { }); it("should handle timeouts of several seconds", function () { - const callback = jest.fn(); + const callback = vi.fn(); callbacks.setTimeout(callback, 2000); expect(callback).not.toHaveBeenCalled(); @@ -75,9 +77,9 @@ describe("realtime-callbacks", function () { }); it("should call multiple callbacks in the right order", function () { - const callback1 = jest.fn(); - const callback2 = jest.fn(); - const callback3 = jest.fn(); + const callback1 = vi.fn(); + const callback2 = vi.fn(); + const callback3 = vi.fn(); callbacks.setTimeout(callback2, 200); callbacks.setTimeout(callback1, 100); callbacks.setTimeout(callback3, 300); @@ -100,8 +102,8 @@ describe("realtime-callbacks", function () { }); it("should treat -ve timeouts the same as a zero timeout", function () { - const callback1 = jest.fn(); - const callback2 = jest.fn(); + const callback1 = vi.fn(); + const callback2 = vi.fn(); // check that cb1 is called before cb2 callback1.mockImplementation(function () { @@ -119,8 +121,8 @@ describe("realtime-callbacks", function () { }); it("should not get confused by chained calls", function () { - const callback2 = jest.fn(); - const callback1 = jest.fn(function () { + const callback2 = vi.fn(); + const callback1 = vi.fn(function () { callbacks.setTimeout(callback2, 0); expect(callback2).not.toHaveBeenCalled(); }); @@ -137,10 +139,10 @@ describe("realtime-callbacks", function () { }); it("should be immune to exceptions", function () { - const callback1 = jest.fn(function () { + const callback1 = vi.fn(function () { throw new Error("prepare to die"); }); - const callback2 = jest.fn(); + const callback2 = vi.fn(); callbacks.setTimeout(callback1, 0); callbacks.setTimeout(callback2, 0); @@ -154,7 +156,7 @@ describe("realtime-callbacks", function () { describe("cancelTimeout", function () { it("should cancel a pending timeout", function () { - const callback = jest.fn(); + const callback = vi.fn(); const k = callbacks.setTimeout(callback, 10); callbacks.clearTimeout(k); tick(11); @@ -162,8 +164,8 @@ describe("realtime-callbacks", function () { }); it("should not affect sooner timeouts", function () { - const callback1 = jest.fn(); - const callback2 = jest.fn(); + const callback1 = vi.fn(); + const callback2 = vi.fn(); callbacks.setTimeout(callback1, 100); const k = callbacks.setTimeout(callback2, 200); diff --git a/spec/unit/receipt-accumulator.spec.ts b/spec/unit/receipt-accumulator.spec.ts index 6e2c71cea83..faee59370b2 100644 --- a/spec/unit/receipt-accumulator.spec.ts +++ b/spec/unit/receipt-accumulator.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { describe, expect, it } from "vitest"; + import { ReceiptType } from "../../src/@types/read_receipts"; import { ReceiptAccumulator } from "../../src/receipt-accumulator"; diff --git a/spec/unit/relations.spec.ts b/spec/unit/relations.spec.ts index a8e1c775b5c..40642573965 100644 --- a/spec/unit/relations.spec.ts +++ b/spec/unit/relations.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { afterEach, describe, expect, it, vi } from "vitest"; + import { M_POLL_START } from "../../src/@types/polls"; import { EventTimelineSet } from "../../src/models/event-timeline-set"; import { MatrixEvent, MatrixEventEvent } from "../../src/models/event"; @@ -25,7 +27,7 @@ import { logger } from "../../src/logger"; describe("Relations", function () { afterEach(() => { - jest.spyOn(logger, "error").mockRestore(); + vi.spyOn(logger, "error").mockRestore(); }); it("should deduplicate annotations", function () { @@ -90,9 +92,9 @@ describe("Relations", function () { it("should not add events without a relation", async () => { // dont pollute console - const logSpy = jest.spyOn(logger, "error").mockImplementation(() => {}); + const logSpy = vi.spyOn(logger, "error").mockImplementation(() => {}); const relations = new Relations(relationType, eventType, room); - const emitSpy = jest.spyOn(relations, "emit"); + const emitSpy = vi.spyOn(relations, "emit"); const event = new MatrixEvent({ type: eventType }); await relations.addEvent(event); @@ -104,9 +106,9 @@ describe("Relations", function () { it("should not add events of incorrect event type", async () => { // dont pollute console - const logSpy = jest.spyOn(logger, "error").mockImplementation(() => {}); + const logSpy = vi.spyOn(logger, "error").mockImplementation(() => {}); const relations = new Relations(relationType, eventType, room); - const emitSpy = jest.spyOn(relations, "emit"); + const emitSpy = vi.spyOn(relations, "emit"); const event = new MatrixEvent({ type: "different-event-type", content: { @@ -127,7 +129,7 @@ describe("Relations", function () { it("adds events that match alt event types", async () => { const relations = new Relations(relationType, eventType, room, altEventTypes); - const emitSpy = jest.spyOn(relations, "emit"); + const emitSpy = vi.spyOn(relations, "emit"); const event = new MatrixEvent({ type: M_POLL_START.unstable!, content: { @@ -146,7 +148,7 @@ describe("Relations", function () { }); it("should not add events of incorrect relation type", async () => { - const logSpy = jest.spyOn(logger, "error").mockImplementation(() => {}); + const logSpy = vi.spyOn(logger, "error").mockImplementation(() => {}); const relations = new Relations(relationType, eventType, room); const event = new MatrixEvent({ type: eventType, @@ -159,7 +161,7 @@ describe("Relations", function () { }); await relations.addEvent(event); - const emitSpy = jest.spyOn(relations, "emit"); + const emitSpy = vi.spyOn(relations, "emit"); expect(logSpy).toHaveBeenCalledWith(`Event relation info doesn't match this container`); // event not added diff --git a/spec/unit/rendezvous/MSC4108RendezvousSession.spec.ts b/spec/unit/rendezvous/MSC4108RendezvousSession.spec.ts index 57afd3afced..d84c67125eb 100644 --- a/spec/unit/rendezvous/MSC4108RendezvousSession.spec.ts +++ b/spec/unit/rendezvous/MSC4108RendezvousSession.spec.ts @@ -14,7 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -import fetchMock from "fetch-mock-jest"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +import fetchMock from "@fetch-mock/vitest"; import { ClientPrefix, IHttpOpts, MatrixClient, MatrixHttpApi } from "../../../src"; import { ClientRendezvousFailureReason, MSC4108RendezvousSession } from "../../../src/rendezvous"; @@ -40,13 +42,7 @@ function makeMockClient(opts: { userId: string; deviceId: string; msc4108Enabled return client; } -fetchMock.config.overwriteRoutes = true; - describe("MSC4108RendezvousSession", () => { - beforeEach(() => { - fetchMock.reset(); - }); - async function postAndCheckLocation(msc4108Enabled: boolean, fallbackRzServer: string, locationResponse: string) { const client = makeMockClient({ userId: "@alice:example.com", deviceId: "DEVICEID", msc4108Enabled }); const transport = new MSC4108RendezvousSession({ client, fallbackRzServer }); @@ -78,7 +74,7 @@ describe("MSC4108RendezvousSession", () => { it("should use custom fetchFn if provided", async () => { const sandbox = fetchMock.sandbox(); - const fetchFn = jest.fn().mockImplementation(sandbox); + const fetchFn = vi.fn().mockImplementation(sandbox); const client = makeMockClient({ userId: "@alice:example.com", deviceId: "DEVICEID", msc4108Enabled: false }); const transport = new MSC4108RendezvousSession({ client, @@ -299,7 +295,7 @@ describe("MSC4108RendezvousSession", () => { it("404 failure callback", async function () { const client = makeMockClient({ userId: "@alice:example.com", deviceId: "DEVICEID", msc4108Enabled: false }); - const onFailure = jest.fn(); + const onFailure = vi.fn(); const transport = new MSC4108RendezvousSession({ client, fallbackRzServer: "https://fallbackserver/rz", @@ -313,7 +309,7 @@ describe("MSC4108RendezvousSession", () => { it("404 failure callback mapped to expired", async function () { const client = makeMockClient({ userId: "@alice:example.com", deviceId: "DEVICEID", msc4108Enabled: false }); - const onFailure = jest.fn(); + const onFailure = vi.fn(); const transport = new MSC4108RendezvousSession({ client, fallbackRzServer: "https://fallbackserver/rz", diff --git a/spec/unit/rendezvous/channels/MSC4108SecureChannel.spec.ts b/spec/unit/rendezvous/channels/MSC4108SecureChannel.spec.ts index bc357d9ac90..bc07e8652fa 100644 --- a/spec/unit/rendezvous/channels/MSC4108SecureChannel.spec.ts +++ b/spec/unit/rendezvous/channels/MSC4108SecureChannel.spec.ts @@ -14,10 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { beforeEach, describe, expect, it, vi } from "vitest"; + import { EstablishedEcies, QrCodeData, QrCodeMode, Ecies } from "@matrix-org/matrix-sdk-crypto-wasm"; -import { mocked } from "jest-mock"; import { MSC4108RendezvousSession, MSC4108SecureChannel, PayloadType } from "../../../../src/rendezvous"; +import { mocked } from "../../../test-utils"; describe("MSC4108SecureChannel", () => { const baseUrl = "https://example.com"; @@ -38,8 +40,8 @@ describe("MSC4108SecureChannel", () => { it("should throw error if attempt to connect multiple times", async () => { const mockSession = { - send: jest.fn(), - receive: jest.fn(), + send: vi.fn(), + receive: vi.fn(), url, } as unknown as MSC4108RendezvousSession; const channel = new MSC4108SecureChannel(mockSession); @@ -56,8 +58,8 @@ describe("MSC4108SecureChannel", () => { it("should throw error on invalid initiate response", async () => { const mockSession = { - send: jest.fn(), - receive: jest.fn(), + send: vi.fn(), + receive: vi.fn(), url, } as unknown as MSC4108RendezvousSession; const channel = new MSC4108SecureChannel(mockSession); @@ -82,8 +84,8 @@ describe("MSC4108SecureChannel", () => { beforeEach(async () => { mockSession = { - send: jest.fn(), - receive: jest.fn(), + send: vi.fn(), + receive: vi.fn(), url, } as unknown as MSC4108RendezvousSession; channel = new MSC4108SecureChannel(mockSession); diff --git a/spec/unit/room-hierarchy.spec.ts b/spec/unit/room-hierarchy.spec.ts index 80570969914..e39631ecd83 100644 --- a/spec/unit/room-hierarchy.spec.ts +++ b/spec/unit/room-hierarchy.spec.ts @@ -14,7 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -import fetchMock from "fetch-mock-jest"; +import { describe, expect, it } from "vitest"; + +import fetchMock from "@fetch-mock/vitest"; import { EventType, MatrixClient, Room } from "../../src"; import { RoomHierarchy } from "../../src/room-hierarchy"; diff --git a/spec/unit/room-member.spec.ts b/spec/unit/room-member.spec.ts index 2b0e223f9fa..7cdcb15c03e 100644 --- a/spec/unit/room-member.spec.ts +++ b/spec/unit/room-member.spec.ts @@ -14,7 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -import fetchMock from "fetch-mock-jest"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + +import fetchMock from "@fetch-mock/vitest"; import * as utils from "../test-utils/test-utils"; import { RoomMember, RoomMemberEvent } from "../../src/models/room-member"; @@ -212,7 +214,7 @@ describe("RoomMember", function () { }); it("should no-op if given a non-state or unrelated event", () => { - const fn = jest.spyOn(member, "emit"); + const fn = vi.spyOn(member, "emit"); expect(fn).not.toHaveBeenCalledWith(RoomMemberEvent.PowerLevel); member.setPowerLevelEvent( utils.mkEvent({ diff --git a/spec/unit/room-state.spec.ts b/spec/unit/room-state.spec.ts index 9e0c29f35e8..0bd56b342e5 100644 --- a/spec/unit/room-state.spec.ts +++ b/spec/unit/room-state.spec.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { MockedObject } from "jest-mock"; +import { beforeEach, describe, expect, it, MockedObject, vi } from "vitest"; import * as utils from "../test-utils/test-utils"; import { makeBeaconEvent, makeBeaconInfoEvent } from "../test-utils/beacon"; @@ -274,8 +274,8 @@ describe("RoomState", function () { }); // spy on the room members - jest.spyOn(state.members[userA], "setPowerLevelEvent"); - jest.spyOn(state.members[userB], "setPowerLevelEvent"); + vi.spyOn(state.members[userA], "setPowerLevelEvent"); + vi.spyOn(state.members[userB], "setPowerLevelEvent"); state.setStateEvents([powerLevelEvent]); expect(state.members[userA].setPowerLevelEvent).toHaveBeenCalledWith(powerLevelEvent); @@ -319,8 +319,8 @@ describe("RoomState", function () { event: true, }); // spy on the room members - jest.spyOn(state.members[userA], "setMembershipEvent"); - jest.spyOn(state.members[userB], "setMembershipEvent"); + vi.spyOn(state.members[userA], "setMembershipEvent"); + vi.spyOn(state.members[userB], "setMembershipEvent"); state.setStateEvents([memberEvent]); expect(state.members[userA].setMembershipEvent).not.toHaveBeenCalled(); @@ -354,7 +354,7 @@ describe("RoomState", function () { describe("beacon events", () => { it("adds new beacon info events to state and emits", () => { const beaconEvent = makeBeaconInfoEvent(userA, roomId); - const emitSpy = jest.spyOn(state, "emit"); + const emitSpy = vi.spyOn(state, "emit"); state.setStateEvents([beaconEvent]); @@ -370,7 +370,7 @@ describe("RoomState", function () { const redactionEvent = new MatrixEvent({ type: "m.room.redaction" }); const room = new Room(roomId, mockClient, userA); redactedBeaconEvent.makeRedacted(redactionEvent, room); - const emitSpy = jest.spyOn(state, "emit"); + const emitSpy = vi.spyOn(state, "emit"); state.setStateEvents([redactedBeaconEvent]); @@ -409,7 +409,7 @@ describe("RoomState", function () { state.setStateEvents([beaconEvent]); const beaconInstance = state.beacons.get(getBeaconInfoIdentifier(beaconEvent)); - const destroySpy = jest.spyOn(beaconInstance as Beacon, "destroy"); + const destroySpy = vi.spyOn(beaconInstance as Beacon, "destroy"); expect(beaconInstance?.isLive).toEqual(true); state.setStateEvents([redactedBeaconEvent]); @@ -422,7 +422,7 @@ describe("RoomState", function () { const liveBeaconEvent = makeBeaconInfoEvent(userA, roomId, { isLive: true }, "$beacon1"); const deadBeaconEvent = makeBeaconInfoEvent(userB, roomId, { isLive: false }, "$beacon2"); - const emitSpy = jest.spyOn(state, "emit"); + const emitSpy = vi.spyOn(state, "emit"); state.setStateEvents([liveBeaconEvent, deadBeaconEvent]); @@ -601,8 +601,8 @@ describe("RoomState", function () { }, }); // spy on the room members - jest.spyOn(state.members[userA], "setTypingEvent"); - jest.spyOn(state.members[userB], "setTypingEvent"); + vi.spyOn(state.members[userA], "setTypingEvent"); + vi.spyOn(state.members[userB], "setTypingEvent"); state.setTypingEvent(typingEvent); expect(state.members[userA].setTypingEvent).toHaveBeenCalledWith(typingEvent); @@ -858,14 +858,14 @@ describe("RoomState", function () { const beacon1 = makeBeaconInfoEvent(userA, roomId, {}, "$beacon1"); const beacon2 = makeBeaconInfoEvent(userB, roomId, {}, "$beacon2"); - const mockClient = { decryptEventIfNeeded: jest.fn() } as unknown as MockedObject; + const mockClient = { decryptEventIfNeeded: vi.fn() } as unknown as MockedObject; beforeEach(() => { mockClient.decryptEventIfNeeded.mockClear(); }); it("does nothing when state has no beacons", () => { - const emitSpy = jest.spyOn(state, "emit"); + const emitSpy = vi.spyOn(state, "emit"); state.processBeaconEvents([makeBeaconEvent(userA, { beaconInfoId: "$beacon1" })], mockClient); expect(emitSpy).not.toHaveBeenCalled(); expect(mockClient.decryptEventIfNeeded).not.toHaveBeenCalled(); @@ -873,7 +873,7 @@ describe("RoomState", function () { it("does nothing when there are no events", () => { state.setStateEvents([beacon1, beacon2]); - const emitSpy = jest.spyOn(state, "emit").mockClear(); + const emitSpy = vi.spyOn(state, "emit").mockClear(); state.processBeaconEvents([], mockClient); expect(emitSpy).not.toHaveBeenCalled(); expect(mockClient.decryptEventIfNeeded).not.toHaveBeenCalled(); @@ -894,7 +894,7 @@ describe("RoomState", function () { }, }); state.setStateEvents([beacon1, beacon2]); - const emitSpy = jest.spyOn(state, "emit").mockClear(); + const emitSpy = vi.spyOn(state, "emit").mockClear(); state.processBeaconEvents([location, otherRelatedEvent], mockClient); expect(emitSpy).not.toHaveBeenCalled(); }); @@ -912,7 +912,7 @@ describe("RoomState", function () { }, }); state.setStateEvents([beacon1, beacon2]); - const emitSpy = jest.spyOn(state, "emit").mockClear(); + const emitSpy = vi.spyOn(state, "emit").mockClear(); state.processBeaconEvents([otherRelatedEvent], mockClient); expect(emitSpy).not.toHaveBeenCalled(); }); @@ -935,7 +935,7 @@ describe("RoomState", function () { expect(state.beacons.size).toEqual(2); const beaconInstance = state.beacons.get(getBeaconInfoIdentifier(beacon1)) as Beacon; - const addLocationsSpy = jest.spyOn(beaconInstance, "addLocations"); + const addLocationsSpy = vi.spyOn(beaconInstance, "addLocations"); await state.processBeaconEvents([location1, location2, location3], mockClient); @@ -963,14 +963,14 @@ describe("RoomState", function () { type: EventType.RoomMessageEncrypted, content: beacon1RelationContent, }); - jest.spyOn(decryptingRelatedEvent, "isBeingDecrypted").mockReturnValue(true); + vi.spyOn(decryptingRelatedEvent, "isBeingDecrypted").mockReturnValue(true); const failedDecryptionRelatedEvent = new MatrixEvent({ sender: userA, type: EventType.RoomMessageEncrypted, content: beacon1RelationContent, }); - jest.spyOn(failedDecryptionRelatedEvent, "isDecryptionFailure").mockReturnValue(true); + vi.spyOn(failedDecryptionRelatedEvent, "isDecryptionFailure").mockReturnValue(true); it("discards events without relations", () => { const unrelatedEvent = new MatrixEvent({ @@ -978,7 +978,7 @@ describe("RoomState", function () { type: EventType.RoomMessageEncrypted, }); state.setStateEvents([beacon1, beacon2]); - const emitSpy = jest.spyOn(state, "emit").mockClear(); + const emitSpy = vi.spyOn(state, "emit").mockClear(); state.processBeaconEvents([unrelatedEvent], mockClient); expect(emitSpy).not.toHaveBeenCalled(); // discard unrelated events early @@ -1002,7 +1002,7 @@ describe("RoomState", function () { state.setStateEvents([beacon1, beacon2]); const beacon = state.beacons.get(getBeaconInfoIdentifier(beacon1)) as Beacon; - const addLocationsSpy = jest.spyOn(beacon, "addLocations").mockClear(); + const addLocationsSpy = vi.spyOn(beacon, "addLocations").mockClear(); state.processBeaconEvents([location, otherRelatedEvent], mockClient); expect(addLocationsSpy).not.toHaveBeenCalled(); // discard unrelated events early @@ -1025,7 +1025,7 @@ describe("RoomState", function () { type: EventType.RoomMessageEncrypted, content: beacon1RelationContent, }); - jest.spyOn(decryptingRelatedEvent, "isBeingDecrypted").mockReturnValue(true); + vi.spyOn(decryptingRelatedEvent, "isBeingDecrypted").mockReturnValue(true); state.setStateEvents([beacon1, beacon2]); await state.processBeaconEvents([decryptingRelatedEvent], mockClient); @@ -1040,12 +1040,12 @@ describe("RoomState", function () { type: EventType.RoomMessageEncrypted, content: beacon1RelationContent, }); - jest.spyOn(failedDecryptionRelatedEvent, "isDecryptionFailure").mockReturnValue(true); + vi.spyOn(failedDecryptionRelatedEvent, "isDecryptionFailure").mockReturnValue(true); mockClient.decryptEventIfNeeded.mockRejectedValue( new DecryptionError(DecryptionFailureCode.UNKNOWN_ERROR, "msg"), ); // spy on event.once - const eventOnceSpy = jest.spyOn(failedDecryptionRelatedEvent, "once"); + const eventOnceSpy = vi.spyOn(failedDecryptionRelatedEvent, "once"); state.setStateEvents([beacon1, beacon2]); await state.processBeaconEvents([failedDecryptionRelatedEvent], mockClient); @@ -1060,10 +1060,10 @@ describe("RoomState", function () { type: EventType.RoomMessageEncrypted, content: beacon1RelationContent, }); - jest.spyOn(decryptingRelatedEvent, "isBeingDecrypted").mockReturnValue(true); + vi.spyOn(decryptingRelatedEvent, "isBeingDecrypted").mockReturnValue(true); state.setStateEvents([beacon1, beacon2]); const beacon = state.beacons.get(getBeaconInfoIdentifier(beacon1)) as Beacon; - const addLocationsSpy = jest.spyOn(beacon, "addLocations").mockClear(); + const addLocationsSpy = vi.spyOn(beacon, "addLocations").mockClear(); await state.processBeaconEvents([decryptingRelatedEvent], mockClient); // this event is a message after decryption @@ -1089,7 +1089,7 @@ describe("RoomState", function () { state.setStateEvents([beacon1, beacon2]); const beacon = state.beacons.get(getBeaconInfoIdentifier(beacon1)) as Beacon; - const addLocationsSpy = jest.spyOn(beacon, "addLocations").mockClear(); + const addLocationsSpy = vi.spyOn(beacon, "addLocations").mockClear(); const prom = state.processBeaconEvents([decryptingRelatedEvent], mockClient); // update type after '''decryption''' @@ -1107,7 +1107,7 @@ describe("RoomState", function () { it("should return false if the user isn't authenticated", () => { expect( state.mayClientSendStateEvent("m.room.message", { - isGuest: jest.fn().mockReturnValue(false), + isGuest: vi.fn().mockReturnValue(false), credentials: {}, } as unknown as MatrixClient), ).toBeFalsy(); @@ -1116,7 +1116,7 @@ describe("RoomState", function () { it("should return false if the user is a guest", () => { expect( state.mayClientSendStateEvent("m.room.message", { - isGuest: jest.fn().mockReturnValue(true), + isGuest: vi.fn().mockReturnValue(true), credentials: { userId: userA }, } as unknown as MatrixClient), ).toBeFalsy(); diff --git a/spec/unit/room.spec.ts b/spec/unit/room.spec.ts index 9a31e492f3c..b2a166f1482 100644 --- a/spec/unit/room.spec.ts +++ b/spec/unit/room.spec.ts @@ -18,7 +18,8 @@ limitations under the License. * This is an internal module. See {@link MatrixClient} for the public class. */ -import { mocked } from "jest-mock"; +import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; + import { M_POLL_KIND_DISCLOSED, M_POLL_RESPONSE, M_POLL_START, Optional, PollStartEvent } from "matrix-events-sdk"; import * as utils from "../test-utils/test-utils"; @@ -59,6 +60,7 @@ import { getMockClientWithEventEmitter, mockClientMethodsUser } from "../test-ut import { logger } from "../../src/logger"; import { flushPromises } from "../test-utils/flushPromises"; import { KnownMembership } from "../../src/@types/membership"; +import { mocked } from "../test-utils"; describe("Room", function () { const roomId = "!foo:bar"; @@ -227,7 +229,7 @@ describe("Room", function () { // @ts-ignore room.currentState = room.getLiveTimeline().endState = utils.mock(RoomState, "currentState"); - jest.spyOn(logger, "warn"); + vi.spyOn(logger, "warn"); }); describe("getCreator", () => { @@ -492,7 +494,7 @@ describe("Room", function () { remoteEvent.event.unsigned = { transaction_id: "TXN_ID" }; const remoteEventId = remoteEvent.getId(); - const stub = jest.fn(); + const stub = vi.fn(); room.on(RoomEvent.LocalEchoUpdated, stub); // first add the local echo @@ -1244,7 +1246,7 @@ describe("Room", function () { }); it("emits an update event", function () { - const spy = jest.fn(); + const spy = vi.fn(); const summary = { "m.heroes": [], "m.invited_member_count": 1, @@ -1499,8 +1501,8 @@ describe("Room", function () { it("should reset the unread count when our non-synthetic receipt points to the latest event", () => { // Given a room with 2 events, and an unread count set. - room.client.isInitialSyncComplete = jest.fn().mockReturnValue(true); - jest.spyOn(room, "timeline", "get").mockReturnValue([event1, event2]); + room.client.isInitialSyncComplete = vi.fn().mockReturnValue(true); + vi.spyOn(room, "timeline", "get").mockReturnValue([event1, event2]); room.setUnread(NotificationCountType.Total, 45); room.setUnread(NotificationCountType.Highlight, 57); // Sanity check: @@ -1518,8 +1520,8 @@ describe("Room", function () { it("should not reset the unread count when someone else's receipt points to the latest event", () => { // Given a room with 2 events, and an unread count set. - room.client.isInitialSyncComplete = jest.fn().mockReturnValue(true); - jest.spyOn(room, "timeline", "get").mockReturnValue([event1, event2]); + room.client.isInitialSyncComplete = vi.fn().mockReturnValue(true); + vi.spyOn(room, "timeline", "get").mockReturnValue([event1, event2]); room.setUnread(NotificationCountType.Total, 45); room.setUnread(NotificationCountType.Highlight, 57); // Sanity check: @@ -1537,8 +1539,8 @@ describe("Room", function () { it("should not reset the unread count when our non-synthetic receipt points to an earlier event", () => { // Given a room with 2 events, and an unread count set. - room.client.isInitialSyncComplete = jest.fn().mockReturnValue(true); - jest.spyOn(room, "timeline", "get").mockReturnValue([event1, event2]); + room.client.isInitialSyncComplete = vi.fn().mockReturnValue(true); + vi.spyOn(room, "timeline", "get").mockReturnValue([event1, event2]); room.setUnread(NotificationCountType.Total, 45); room.setUnread(NotificationCountType.Highlight, 57); // Sanity check: @@ -1556,8 +1558,8 @@ describe("Room", function () { it("should not reset the unread count when our a synthetic receipt points to the latest event", () => { // Given a room with 2 events, and an unread count set. - room.client.isInitialSyncComplete = jest.fn().mockReturnValue(true); - jest.spyOn(room, "timeline", "get").mockReturnValue([event1, event2]); + room.client.isInitialSyncComplete = vi.fn().mockReturnValue(true); + vi.spyOn(room, "timeline", "get").mockReturnValue([event1, event2]); room.setUnread(NotificationCountType.Total, 45); room.setUnread(NotificationCountType.Highlight, 57); // Sanity check: @@ -1589,7 +1591,7 @@ describe("Room", function () { }); it("should emit an event when a receipt is added", function () { - const listener = jest.fn(); + const listener = vi.fn(); room.on(RoomEvent.Receipt, listener); const ts = 13787898424; @@ -1787,7 +1789,7 @@ describe("Room", function () { const ts = 13787898424; room.addLiveEvents([eventToAck], { addToState: false }); room.addReceipt(mkReceipt(roomId, [mkRecord(eventToAck.getId()!, "m.read", userB, ts)])); - room.findEventById = jest.fn().mockReturnValue({ getThread: jest.fn() } as unknown as MatrixEvent); + room.findEventById = vi.fn().mockReturnValue({ getThread: vi.fn() } as unknown as MatrixEvent); expect(room.hasUserReadEvent(userB, eventToAck.getId()!)).toEqual(true); }); @@ -1825,11 +1827,11 @@ describe("Room", function () { describe("threads enabled", () => { beforeEach(() => { - jest.spyOn(room.client, "supportsThreads").mockReturnValue(true); + vi.spyOn(room.client, "supportsThreads").mockReturnValue(true); }); afterEach(() => { - jest.restoreAllMocks(); + vi.restoreAllMocks(); }); it("returns true if there is an unthreaded receipt for a later event in a thread", async () => { @@ -1873,7 +1875,7 @@ describe("Room", function () { ); it("should emit Room.tags event when new tags are " + "received on the event stream", function () { - const listener = jest.fn(); + const listener = vi.fn(); room.on(RoomEvent.Tags, listener); const tags = { "m.foo": { order: 0.5 } }; @@ -2073,7 +2075,7 @@ describe("Room", function () { isRoomEncrypted: function () { return false; }, - members: jest.fn().mockImplementation(() => { + members: vi.fn().mockImplementation(() => { if (serverResponse instanceof Error) { return Promise.reject(serverResponse); } else { @@ -2095,8 +2097,8 @@ describe("Room", function () { return Promise.resolve(); }, getSyncToken: () => "sync_token", - getPendingEvents: jest.fn().mockResolvedValue([]), - setPendingEvents: jest.fn().mockResolvedValue(undefined), + getPendingEvents: vi.fn().mockResolvedValue([]), + setPendingEvents: vi.fn().mockResolvedValue(undefined), }, }; } @@ -2928,7 +2930,7 @@ describe("Room", function () { await room.addLiveEvents([threadRoot], { addToState: false }); - const onEvent = jest.fn(); + const onEvent = vi.fn(); room.on(RoomEvent.Timeline, onEvent); await room.addLiveEvents([threadResponse1], { addToState: false }); @@ -3042,7 +3044,7 @@ describe("Room", function () { }, }); - room.client.fetchRelations = jest.fn().mockResolvedValue({ + room.client.fetchRelations = vi.fn().mockResolvedValue({ chunk: [threadResponse2Reaction.event, threadResponse2.event, threadResponse1.event], }); @@ -3426,7 +3428,7 @@ describe("Room", function () { describe("invalid receipts", () => { beforeEach(() => { // Clear the spies on logger.warn - jest.clearAllMocks(); + vi.clearAllMocks(); }); it("ignores receipts pointing at missing events", () => { @@ -3435,7 +3437,7 @@ describe("Room", function () { return { eventId: "missingEventId" } as WrappedReceipt; }; // But the event ID it contains does not refer to an event we have - room.findEventById = jest.fn().mockReturnValue(null); + room.findEventById = vi.fn().mockReturnValue(null); // When we ask what they have read // Then we say "nothing" @@ -3448,7 +3450,7 @@ describe("Room", function () { return { eventId: "wrongThreadEventId", data: { ts: 0, thread_id: "thread1" } } as WrappedReceipt; }; // But the event it refers to is in a thread - room.findEventById = jest.fn().mockReturnValue({ threadRootId: "thread2" } as MatrixEvent); + room.findEventById = vi.fn().mockReturnValue({ threadRootId: "thread2" } as MatrixEvent); // When we ask what they have read // Then we say "nothing" @@ -3465,7 +3467,7 @@ describe("Room", function () { return { eventId: "inThreadEventId" } as WrappedReceipt; }; // And the event it refers to is in a thread - room.findEventById = jest.fn().mockReturnValue({ threadRootId: "thread2" } as MatrixEvent); + room.findEventById = vi.fn().mockReturnValue({ threadRootId: "thread2" } as MatrixEvent); // When we ask what they have read // Then we say the event @@ -3478,7 +3480,7 @@ describe("Room", function () { return { eventId: "mainThreadEventId", data: { ts: 12, thread_id: "main" } } as WrappedReceipt; }; // And the event it refers to is in a thread - room.findEventById = jest.fn().mockReturnValue({ threadRootId: undefined } as MatrixEvent); + room.findEventById = vi.fn().mockReturnValue({ threadRootId: undefined } as MatrixEvent); // When we ask what they have read // Then we say the event @@ -3491,7 +3493,7 @@ describe("Room", function () { return { eventId: "rootId", data: { ts: 12, thread_id: "main" } } as WrappedReceipt; }; // And the event it refers to is in a thread, because it is a thread root - room.findEventById = jest + room.findEventById = vi .fn() .mockReturnValue({ isThreadRoot: true, threadRootId: "thread1" } as MatrixEvent); @@ -3504,7 +3506,7 @@ describe("Room", function () { describe("valid receipts", () => { beforeEach(() => { // When we look up the event referred to by the receipt, it exists - room.findEventById = jest.fn().mockReturnValue({} as MatrixEvent); + room.findEventById = vi.fn().mockReturnValue({} as MatrixEvent); }); it("handles missing receipt type", () => { @@ -3532,7 +3534,7 @@ describe("Room", function () { compareEventOrdering: (event1: string, _event2: string) => { return event1 === `eventId${i}` ? 1 : -1; }, - findEventById: jest.fn().mockReturnValue({} as MatrixEvent), + findEventById: vi.fn().mockReturnValue({} as MatrixEvent), }) as unknown as EventTimelineSet; expect(room.getEventReadUpTo(userA)).toEqual(`eventId${i}`); @@ -3545,7 +3547,7 @@ describe("Room", function () { room.getUnfilteredTimelineSet = () => ({ compareEventOrdering: () => null, - findEventById: jest.fn().mockReturnValue({} as MatrixEvent), + findEventById: vi.fn().mockReturnValue({} as MatrixEvent), }) as unknown as EventTimelineSet; room.getReadReceiptForUserId = (userId, ignore, receiptType): WrappedReceipt | null => { if (receiptType === ReceiptType.ReadPrivate) { @@ -3565,7 +3567,7 @@ describe("Room", function () { room.getUnfilteredTimelineSet = () => ({ compareEventOrdering: () => null, - findEventById: jest.fn().mockReturnValue({} as MatrixEvent), + findEventById: vi.fn().mockReturnValue({} as MatrixEvent), }) as unknown as EventTimelineSet; room.getReadReceiptForUserId = (userId, ignore, receiptType): WrappedReceipt | null => { if (receiptType === ReceiptType.Read) { @@ -3583,7 +3585,7 @@ describe("Room", function () { room.getUnfilteredTimelineSet = () => ({ compareEventOrdering: () => null, - findEventById: jest.fn().mockReturnValue({} as MatrixEvent), + findEventById: vi.fn().mockReturnValue({} as MatrixEvent), }) as unknown as EventTimelineSet; }); @@ -3618,11 +3620,11 @@ describe("Room", function () { describe("roomNameGenerator", () => { const client = new TestClient(userA).client; - client.roomNameGenerator = jest.fn().mockReturnValue(null); + client.roomNameGenerator = vi.fn().mockReturnValue(null); const room = new Room(roomId, client, userA); it("should call fn when recalculating room name", () => { - (client.roomNameGenerator as jest.Mock).mockClear(); + mocked(client.roomNameGenerator!).mockClear(); room.recalculate(); expect(client.roomNameGenerator).toHaveBeenCalled(); }); @@ -3677,7 +3679,7 @@ describe("Room", function () { }); it("emits event on notifications reset", () => { - const cb = jest.fn(); + const cb = vi.fn(); room.on(RoomEvent.UnreadNotifications, cb); @@ -3745,7 +3747,7 @@ describe("Room", function () { }); it("retains highlight for encrypted rooms on reset", () => { - room.hasEncryptionStateEvent = jest.fn().mockReturnValue(true); + room.hasEncryptionStateEvent = vi.fn().mockReturnValue(true); room.setThreadUnreadNotificationCount("$123", NotificationCountType.Total, 2); room.setThreadUnreadNotificationCount("$456", NotificationCountType.Total, 1); @@ -3758,7 +3760,7 @@ describe("Room", function () { }); it("resets highlight for unencrypted rooms on reset", () => { - room.hasEncryptionStateEvent = jest.fn().mockReturnValue(false); + room.hasEncryptionStateEvent = vi.fn().mockReturnValue(false); room.setThreadUnreadNotificationCount("$123", NotificationCountType.Total, 2); room.setThreadUnreadNotificationCount("$456", NotificationCountType.Total, 1); @@ -3775,9 +3777,9 @@ describe("Room", function () { it("should load pending events from from the store and decrypt if needed", async () => { const client = new TestClient(userA).client; client.crypto = client["cryptoBackend"] = { - decryptEvent: jest.fn().mockResolvedValue({ clearEvent: { body: "enc" } }), + decryptEvent: vi.fn().mockResolvedValue({ clearEvent: { body: "enc" } }), } as unknown as Crypto; - client.store.getPendingEvents = jest.fn(async (roomId) => [ + client.store.getPendingEvents = vi.fn(async (roomId) => [ { event_id: "$1:server", type: "m.room.message", @@ -3830,10 +3832,10 @@ describe("Room", function () { beforeEach(() => { client = getMockClientWithEventEmitter({ - decryptEventIfNeeded: jest.fn(), + decryptEventIfNeeded: vi.fn(), }); room = new Room(roomId, client, userA); - jest.spyOn(room, "emit").mockClear(); + vi.spyOn(room, "emit").mockClear(); }); const makePollStart = (id: string): MatrixEvent => { @@ -3896,8 +3898,8 @@ describe("Room", function () { const poll = room.polls.get(pollStartEvent.getId()!)!; const poll2 = room.polls.get(pollStartEvent2.getId()!)!; - jest.spyOn(poll, "onNewRelation"); - jest.spyOn(poll2, "onNewRelation"); + vi.spyOn(poll, "onNewRelation"); + vi.spyOn(poll2, "onNewRelation"); await room.processPollEvents([errorEvent, messageEvent, pollResponseEvent]); @@ -3915,7 +3917,7 @@ describe("Room", function () { const pollStartEventId = "poll1"; const pollStartEvent = makePollStart(pollStartEventId); // simulate decryption failure - const isDecryptionFailureSpy = jest.spyOn(pollStartEvent, "isDecryptionFailure").mockReturnValue(true); + const isDecryptionFailureSpy = vi.spyOn(pollStartEvent, "isDecryptionFailure").mockReturnValue(true); await room.processPollEvents([pollStartEvent]); // do not expect a poll to show up for the room @@ -3958,8 +3960,8 @@ describe("Room", function () { beforeEach(() => { client = getMockClientWithEventEmitter({ ...mockClientMethodsUser(), - isInitialSyncComplete: jest.fn().mockReturnValue(false), - supportsThreads: jest.fn().mockReturnValue(true), + isInitialSyncComplete: vi.fn().mockReturnValue(false), + supportsThreads: vi.fn().mockReturnValue(true), }); }); @@ -4177,7 +4179,7 @@ describe("Room", function () { describe("getRecommendedVersion", () => { it("returns the server's recommended version from capabilities", async () => { const client = new TestClient(userA).client; - client.getCapabilities = jest.fn().mockReturnValue({ + client.getCapabilities = vi.fn().mockReturnValue({ ["m.room_versions"]: { default: "1", available: ["1", "2"], @@ -4193,14 +4195,14 @@ describe("Room", function () { it("force-refreshes versions to make sure an upgrade is necessary", async () => { const client = new TestClient(userA).client; - client.getCapabilities = jest.fn().mockReturnValue({ + client.getCapabilities = vi.fn().mockReturnValue({ ["m.room_versions"]: { default: "5", available: ["5"], }, }); - client.fetchCapabilities = jest.fn().mockResolvedValue({ + client.fetchCapabilities = vi.fn().mockResolvedValue({ ["m.room_versions"]: { default: "1", available: ["1"], diff --git a/spec/unit/rust-crypto/CrossSigningIdentity.spec.ts b/spec/unit/rust-crypto/CrossSigningIdentity.spec.ts index 6c7b33ea03f..65ad964e045 100644 --- a/spec/unit/rust-crypto/CrossSigningIdentity.spec.ts +++ b/spec/unit/rust-crypto/CrossSigningIdentity.spec.ts @@ -14,7 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { Mocked } from "jest-mock"; +import { beforeEach, describe, expect, it, vi, Mocked } from "vitest"; + import * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-wasm"; import { CrossSigningIdentity } from "../../../src/rust-crypto/CrossSigningIdentity"; @@ -39,20 +40,20 @@ describe("CrossSigningIdentity", () => { await RustSdkCryptoJs.initAsync(); olmMachine = { - crossSigningStatus: jest.fn(), - bootstrapCrossSigning: jest.fn(), - exportCrossSigningKeys: jest.fn(), - close: jest.fn(), + crossSigningStatus: vi.fn(), + bootstrapCrossSigning: vi.fn(), + exportCrossSigningKeys: vi.fn(), + close: vi.fn(), } as unknown as Mocked; outgoingRequestProcessor = { - makeOutgoingRequest: jest.fn(), + makeOutgoingRequest: vi.fn(), } as unknown as Mocked; secretStorage = { - get: jest.fn(), - hasKey: jest.fn(), - store: jest.fn(), + get: vi.fn(), + hasKey: vi.fn(), + store: vi.fn(), } as unknown as Mocked; crossSigning = new CrossSigningIdentity(olmMachine, outgoingRequestProcessor, secretStorage); diff --git a/spec/unit/rust-crypto/KeyClaimManager.spec.ts b/spec/unit/rust-crypto/KeyClaimManager.spec.ts index f2ad04cacfd..3bd1eb4b650 100644 --- a/spec/unit/rust-crypto/KeyClaimManager.spec.ts +++ b/spec/unit/rust-crypto/KeyClaimManager.spec.ts @@ -14,9 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { afterEach, beforeEach, describe, expect, it, vi, Mocked } from "vitest"; + import * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-wasm"; -import fetchMock from "fetch-mock-jest"; -import { Mocked } from "jest-mock"; +import fetchMock from "@fetch-mock/vitest"; import { KeysClaimRequest, UserId } from "@matrix-org/matrix-sdk-crypto-wasm"; import { OutgoingRequestProcessor } from "../../../src/rust-crypto/OutgoingRequestProcessor"; @@ -49,8 +50,8 @@ describe("KeyClaimManager", () => { }); olmMachine = { - getMissingSessions: jest.fn(), - markRequestAsSent: jest.fn(), + getMissingSessions: vi.fn(), + markRequestAsSent: vi.fn(), } as unknown as Mocked; const outgoingRequestProcessor = new OutgoingRequestProcessor(olmMachine, httpApi); diff --git a/spec/unit/rust-crypto/OutgoingRequestProcessor.spec.ts b/spec/unit/rust-crypto/OutgoingRequestProcessor.spec.ts index 959917b131f..49be385ed43 100644 --- a/spec/unit/rust-crypto/OutgoingRequestProcessor.spec.ts +++ b/spec/unit/rust-crypto/OutgoingRequestProcessor.spec.ts @@ -14,8 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { afterEach, beforeEach, describe, expect, it, test, vi, Mocked } from "vitest"; + import MockHttpBackend from "matrix-mock-request"; -import { Mocked } from "jest-mock"; import * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-wasm"; import { KeysBackupRequest, @@ -28,7 +29,7 @@ import { UploadSigningKeysRequest, ToDeviceRequest, } from "@matrix-org/matrix-sdk-crypto-wasm"; -import fetchMock from "fetch-mock-jest"; +import fetchMock from "@fetch-mock/vitest"; import { TypedEventEmitter } from "../../../src"; import { HttpApiEvent, HttpApiEventHandlerMap, IHttpOpts, MatrixHttpApi, UIAuthCallback } from "../../../src"; @@ -66,7 +67,7 @@ describe("OutgoingRequestProcessor", () => { }); olmMachine = { - markRequestAsSent: jest.fn(), + markRequestAsSent: vi.fn(), } as unknown as Mocked; processor = new OutgoingRequestProcessor(olmMachine, httpApi); @@ -308,7 +309,7 @@ describe("OutgoingRequestProcessor", () => { describe("Should retry requests", () => { beforeEach(() => { - jest.useFakeTimers(); + vi.useFakeTimers(); // here we use another httpApi instance in order to use fetchMock const dummyEventEmitter = new TypedEventEmitter(); @@ -322,8 +323,7 @@ describe("OutgoingRequestProcessor", () => { }); afterEach(() => { - jest.useRealTimers(); - fetchMock.reset(); + vi.useRealTimers(); }); describe("Should retry on retryable errors", () => { @@ -352,7 +352,7 @@ describe("OutgoingRequestProcessor", () => { const requestPromise = processor.makeOutgoingRequest(outgoingRequest); // Run all timers and wait for the request promise to resolve/reject - await Promise.all([jest.runAllTimersAsync(), requestPromise.catch(() => {})]); + await Promise.all([vi.runAllTimersAsync(), requestPromise.catch(() => {})]); await expect(requestPromise).rejects.toThrow(); @@ -380,7 +380,7 @@ describe("OutgoingRequestProcessor", () => { const requestPromise = processor.makeOutgoingRequest(outgoingRequest); - await Promise.all([requestPromise.catch(() => {}), jest.runAllTimersAsync()]); + await Promise.all([requestPromise.catch(() => {}), vi.runAllTimersAsync()]); await expect(requestPromise).rejects.toThrow(); @@ -409,7 +409,7 @@ describe("OutgoingRequestProcessor", () => { const requestPromise = processor.makeOutgoingRequest(outgoingRequest); - await Promise.all([requestPromise, jest.runAllTimersAsync()]); + await Promise.all([requestPromise, vi.runAllTimersAsync()]); const calls = fetchMock.calls("path:/_matrix/client/v3/keys/upload"); expect(calls).toHaveLength(2); @@ -435,7 +435,7 @@ describe("OutgoingRequestProcessor", () => { const requestPromise = processor.makeOutgoingRequest(outgoingRequest); - await Promise.all([requestPromise, jest.runAllTimersAsync()]); + await Promise.all([requestPromise, vi.runAllTimersAsync()]); const calls = fetchMock.calls("express:/_matrix/client/v3/sendToDevice/:type/:txnId"); expect(calls).toHaveLength(2); @@ -463,7 +463,7 @@ describe("OutgoingRequestProcessor", () => { }; const requestPromise = processor.makeOutgoingRequest(outgoingRequest, authCallback); - await Promise.all([requestPromise, jest.runAllTimersAsync()]); + await Promise.all([requestPromise, vi.runAllTimersAsync()]); const calls = fetchMock.calls("path:/_matrix/client/v3/keys/device_signing/upload"); expect(calls).toHaveLength(2); @@ -498,7 +498,7 @@ describe("OutgoingRequestProcessor", () => { const requestPromise = processor.makeOutgoingRequest(outgoingRequest); // advanced by less than the retryAfterMs - await jest.advanceTimersByTimeAsync(retryAfterMs - 1000); + await vi.advanceTimersByTimeAsync(retryAfterMs - 1000); // should not have made a second request yet { @@ -507,7 +507,7 @@ describe("OutgoingRequestProcessor", () => { } // advanced by the remaining time - await jest.advanceTimersByTimeAsync(retryAfterMs + 1000); + await vi.advanceTimersByTimeAsync(retryAfterMs + 1000); await requestPromise; @@ -536,7 +536,7 @@ describe("OutgoingRequestProcessor", () => { const requestPromise = processor.makeOutgoingRequest(outgoingRequest); // Run all timers and wait for the request promise to resolve/reject - await Promise.all([jest.runAllTimersAsync(), requestPromise.catch(() => {})]); + await Promise.all([vi.runAllTimersAsync(), requestPromise.catch(() => {})]); await expect(requestPromise).rejects.toThrow(); @@ -562,7 +562,7 @@ describe("OutgoingRequestProcessor", () => { const requestPromise = processor.makeOutgoingRequest(outgoingRequest); // Run all timers and wait for the request promise to resolve/reject - await Promise.all([jest.runAllTimersAsync(), requestPromise.catch(() => {})]); + await Promise.all([vi.runAllTimersAsync(), requestPromise.catch(() => {})]); await expect(requestPromise).rejects.toThrow(); @@ -595,7 +595,7 @@ describe("OutgoingRequestProcessor", () => { const requestPromise = processor.makeOutgoingRequest(outgoingRequest); - await Promise.all([requestPromise, jest.runAllTimersAsync()]); + await Promise.all([requestPromise, vi.runAllTimersAsync()]); const calls = fetchMock.calls("path:/_matrix/client/v3/keys/upload"); expect(calls).toHaveLength(successfulCall); diff --git a/spec/unit/rust-crypto/OutgoingRequestsManager.spec.ts b/spec/unit/rust-crypto/OutgoingRequestsManager.spec.ts index 9c59c2f416b..5252612fbf5 100644 --- a/spec/unit/rust-crypto/OutgoingRequestsManager.spec.ts +++ b/spec/unit/rust-crypto/OutgoingRequestsManager.spec.ts @@ -14,7 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { Mocked } from "jest-mock"; +import { beforeEach, describe, expect, it, vi, Mocked } from "vitest"; + import * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-wasm"; import { OutgoingRequest, OutgoingRequestProcessor } from "../../../src/rust-crypto/OutgoingRequestProcessor"; @@ -34,11 +35,11 @@ describe("OutgoingRequestsManager", () => { beforeEach(async () => { olmMachine = { - outgoingRequests: jest.fn(), + outgoingRequests: vi.fn(), } as unknown as Mocked; processor = { - makeOutgoingRequest: jest.fn(), + makeOutgoingRequest: vi.fn(), } as unknown as Mocked; manager = new OutgoingRequestsManager(logger, olmMachine, processor); diff --git a/spec/unit/rust-crypto/PerSessionKeyBackupDownloader.spec.ts b/spec/unit/rust-crypto/PerSessionKeyBackupDownloader.spec.ts index 5a4fe4bf72a..5e35fca91a5 100644 --- a/spec/unit/rust-crypto/PerSessionKeyBackupDownloader.spec.ts +++ b/spec/unit/rust-crypto/PerSessionKeyBackupDownloader.spec.ts @@ -14,10 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { Mocked, SpyInstance } from "jest-mock"; +import { afterEach, beforeEach, describe, expect, it, Mocked, MockInstance, vi } from "vitest"; + import * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-wasm"; import { OlmMachine } from "@matrix-org/matrix-sdk-crypto-wasm"; -import fetchMock from "fetch-mock-jest"; +import fetchMock from "@fetch-mock/vitest"; import { PerSessionKeyBackupDownloader } from "../../../src/rust-crypto/PerSessionKeyBackupDownloader"; import { logger } from "../../../src/logger"; @@ -80,7 +81,7 @@ describe("PerSessionKeyBackupDownloader", () => { }); mockBackupDecryptor = { - decryptSessions: jest.fn(), + decryptSessions: vi.fn(), } as unknown as Mocked; mockBackupDecryptor.decryptSessions.mockImplementation(async (ciphertexts) => { @@ -89,20 +90,20 @@ describe("PerSessionKeyBackupDownloader", () => { }); mockRustBackupManager = { - getActiveBackupVersion: jest.fn(), - getServerBackupInfo: jest.fn(), - importBackedUpRoomKeys: jest.fn(), - createBackupDecryptor: jest.fn().mockReturnValue(mockBackupDecryptor), - on: jest.fn().mockImplementation((event, listener) => { + getActiveBackupVersion: vi.fn(), + getServerBackupInfo: vi.fn(), + importBackedUpRoomKeys: vi.fn(), + createBackupDecryptor: vi.fn().mockReturnValue(mockBackupDecryptor), + on: vi.fn().mockImplementation((event, listener) => { mockEmitter.on(event, listener); }), - off: jest.fn().mockImplementation((event, listener) => { + off: vi.fn().mockImplementation((event, listener) => { mockEmitter.off(event, listener); }), } as unknown as Mocked; mockOlmMachine = { - getBackupKeys: jest.fn(), + getBackupKeys: vi.fn(), } as unknown as Mocked; downloader = new PerSessionKeyBackupDownloader(logger, mockOlmMachine, mockHttp, mockRustBackupManager); @@ -117,14 +118,14 @@ describe("PerSessionKeyBackupDownloader", () => { } }); - jest.useFakeTimers(); + vi.useFakeTimers(); }); afterEach(() => { expectedSession = {}; downloader.stop(); fetchMock.mockReset(); - jest.useRealTimers(); + vi.useRealTimers(); }); describe("Given valid backup available", () => { @@ -180,7 +181,7 @@ describe("PerSessionKeyBackupDownloader", () => { }); // @ts-ignore access to private function - const spy = jest.spyOn(downloader, "queryKeyBackup"); + const spy = vi.spyOn(downloader, "queryKeyBackup"); // Call 3 times for same key downloader.onDecryptionKeyMissingError("!roomId", "sessionId"); @@ -208,17 +209,17 @@ describe("PerSessionKeyBackupDownloader", () => { fetchMock.get(`path:/_matrix/client/v3/room_keys/keys/!roomA/sessionA1`, mockCipherKey); // @ts-ignore access to private function - const spy: SpyInstance = jest.spyOn(downloader, "queryKeyBackup"); + const spy: SpyInstance = vi.spyOn(downloader, "queryKeyBackup"); const expectImported = expectSessionImported("!roomA", "sessionA1"); downloader.onDecryptionKeyMissingError("!roomA", "sessionA0"); - await jest.runAllTimersAsync(); + await vi.runAllTimersAsync(); expect(spy).toHaveBeenCalledTimes(1); expect(spy).toHaveLastReturnedWith(Promise.resolve({ ok: false, error: "MISSING_DECRYPTION_KEY" })); downloader.onDecryptionKeyMissingError("!roomA", "sessionA1"); - await jest.runAllTimersAsync(); + await vi.runAllTimersAsync(); expect(spy).toHaveBeenCalledTimes(2); await expectImported; @@ -234,10 +235,10 @@ describe("PerSessionKeyBackupDownloader", () => { }); // @ts-ignore access to private function - const spy: SpyInstance = jest.spyOn(downloader, "queryKeyBackup"); + const spy: SpyInstance = vi.spyOn(downloader, "queryKeyBackup"); downloader.onDecryptionKeyMissingError("!roomA", "sessionA0"); - await jest.runAllTimersAsync(); + await vi.runAllTimersAsync(); expect(spy).toHaveBeenCalledTimes(1); const returnedPromise = spy.mock.results[0].value; @@ -245,15 +246,15 @@ describe("PerSessionKeyBackupDownloader", () => { // Should not query again for a key not in backup downloader.onDecryptionKeyMissingError("!roomA", "sessionA0"); - await jest.runAllTimersAsync(); + await vi.runAllTimersAsync(); expect(spy).toHaveBeenCalledTimes(1); // advance time to retry - jest.advanceTimersByTime(BACKOFF_TIME + 10); + vi.advanceTimersByTime(BACKOFF_TIME + 10); downloader.onDecryptionKeyMissingError("!roomA", "sessionA0"); - await jest.runAllTimersAsync(); + await vi.runAllTimersAsync(); expect(spy).toHaveBeenCalledTimes(2); await expect(spy.mock.results[1].value).rejects.toThrow( @@ -284,7 +285,7 @@ describe("PerSessionKeyBackupDownloader", () => { blockOnServerRequest.resolve(); // let the first request complete - await jest.runAllTimersAsync(); + await vi.runAllTimersAsync(); expect(mockRustBackupManager.importBackedUpRoomKeys).not.toHaveBeenCalled(); expect( @@ -294,14 +295,14 @@ describe("PerSessionKeyBackupDownloader", () => { }); describe("Given no usable backup available", () => { - let getConfigSpy: SpyInstance; + let getConfigSpy: MockInstance; beforeEach(async () => { mockRustBackupManager.getActiveBackupVersion.mockResolvedValue(null); mockOlmMachine.getBackupKeys.mockResolvedValue(null); // @ts-ignore access to private function - getConfigSpy = jest.spyOn(downloader, "getOrCreateBackupConfiguration"); + getConfigSpy = vi.spyOn(downloader, "getOrCreateBackupConfiguration"); }); it("Should not query server if no backup", async () => { @@ -312,7 +313,7 @@ describe("PerSessionKeyBackupDownloader", () => { downloader.onDecryptionKeyMissingError("!roomId", "sessionId"); - await jest.runAllTimersAsync(); + await vi.runAllTimersAsync(); expect(getConfigSpy).toHaveBeenCalledTimes(1); expect(getConfigSpy).toHaveReturnedWith(Promise.resolve(null)); @@ -330,7 +331,7 @@ describe("PerSessionKeyBackupDownloader", () => { downloader.onDecryptionKeyMissingError("!roomId", "sessionId"); - await jest.runAllTimersAsync(); + await vi.runAllTimersAsync(); expect(getConfigSpy).toHaveBeenCalledTimes(1); expect(getConfigSpy).toHaveReturnedWith(Promise.resolve(null)); @@ -349,7 +350,7 @@ describe("PerSessionKeyBackupDownloader", () => { downloader.onDecryptionKeyMissingError("!roomId", "sessionId"); - await jest.runAllTimersAsync(); + await vi.runAllTimersAsync(); expect(getConfigSpy).toHaveBeenCalledTimes(1); expect(getConfigSpy).toHaveReturnedWith(Promise.resolve(null)); @@ -371,7 +372,7 @@ describe("PerSessionKeyBackupDownloader", () => { downloader.onDecryptionKeyMissingError("!roomId", "sessionId"); - await jest.runAllTimersAsync(); + await vi.runAllTimersAsync(); expect(getConfigSpy).toHaveBeenCalledTimes(1); expect(getConfigSpy).toHaveReturnedWith(Promise.resolve(null)); @@ -393,7 +394,7 @@ describe("PerSessionKeyBackupDownloader", () => { downloader.onDecryptionKeyMissingError("!roomId", "sessionId"); - await jest.runAllTimersAsync(); + await vi.runAllTimersAsync(); expect(getConfigSpy).toHaveBeenCalledTimes(1); expect(getConfigSpy).toHaveReturnedWith(Promise.resolve(null)); @@ -424,7 +425,7 @@ describe("PerSessionKeyBackupDownloader", () => { downloader.onDecryptionKeyMissingError("!roomA", "sessionA1"); downloader.onDecryptionKeyMissingError("!roomB", "sessionB1"); downloader.onDecryptionKeyMissingError("!roomC", "sessionC1"); - await jest.runAllTimersAsync(); + await vi.runAllTimersAsync(); // @ts-ignore access to private property expect(downloader.hasConfigurationProblem).toEqual(true); @@ -441,7 +442,7 @@ describe("PerSessionKeyBackupDownloader", () => { // In that case the sdk would fire a backup status update mockEmitter.emit(CryptoEvent.KeyBackupStatus, true); - await jest.runAllTimersAsync(); + await vi.runAllTimersAsync(); expect(downloader.isKeyBackupDownloadConfigured()).toBe(true); await a0Imported; @@ -466,18 +467,14 @@ describe("PerSessionKeyBackupDownloader", () => { it("Should wait on rate limit error", async () => { // simulate rate limit error - fetchMock.get( - `express:/_matrix/client/v3/room_keys/keys/:roomId/:sessionId`, - { - status: 429, - body: { - errcode: "M_LIMIT_EXCEEDED", - error: "Too many requests", - retry_after_ms: 5000, - }, + fetchMock.get(`express:/_matrix/client/v3/room_keys/keys/:roomId/:sessionId`, { + status: 429, + body: { + errcode: "M_LIMIT_EXCEEDED", + error: "Too many requests", + retry_after_ms: 5000, }, - { overwriteRoutes: true }, - ); + }); const keyImported = expectSessionImported("!roomA", "sessionA0"); @@ -485,7 +482,7 @@ describe("PerSessionKeyBackupDownloader", () => { const originalImplementation = downloader.queryKeyBackup.bind(downloader); // @ts-ignore access to private function - const keyQuerySpy: SpyInstance = jest.spyOn(downloader, "queryKeyBackup"); + const keyQuerySpy: SpyInstance = vi.spyOn(downloader, "queryKeyBackup"); const rateDeferred = defer(); keyQuerySpy.mockImplementation( @@ -509,19 +506,17 @@ describe("PerSessionKeyBackupDownloader", () => { "Failed to get key from backup: rate limited", ); - fetchMock.get(`express:/_matrix/client/v3/room_keys/keys/:roomId/:sessionId`, mockCipherKey, { - overwriteRoutes: true, - }); + fetchMock.get(`express:/_matrix/client/v3/room_keys/keys/:roomId/:sessionId`, mockCipherKey); // Advance less than the retry_after_ms - jest.advanceTimersByTime(100); + vi.advanceTimersByTime(100); // let any pending callbacks in PromiseJobs run await Promise.resolve(); // no additional call should have been made expect(keyQuerySpy).toHaveBeenCalledTimes(1); // The loop should resume after the retry_after_ms - jest.advanceTimersByTime(5000); + vi.advanceTimersByTime(5000); // let any pending callbacks in PromiseJobs run await Promise.resolve(); @@ -539,7 +534,7 @@ describe("PerSessionKeyBackupDownloader", () => { const originalImplementation = downloader.queryKeyBackup.bind(downloader); // @ts-ignore - const keyQuerySpy: SpyInstance = jest.spyOn(downloader, "queryKeyBackup"); + const keyQuerySpy: SpyInstance = vi.spyOn(downloader, "queryKeyBackup"); const errorDeferred = defer(); keyQuerySpy.mockImplementation( @@ -565,19 +560,17 @@ describe("PerSessionKeyBackupDownloader", () => { "Failed to get key from backup: NETWORK_ERROR", ); - fetchMock.get(`express:/_matrix/client/v3/room_keys/keys/:roomId/:sessionId`, mockCipherKey, { - overwriteRoutes: true, - }); + fetchMock.get(`express:/_matrix/client/v3/room_keys/keys/:roomId/:sessionId`, mockCipherKey); // Advance less than the retry_after_ms - jest.advanceTimersByTime(100); + vi.advanceTimersByTime(100); // let any pending callbacks in PromiseJobs run await Promise.resolve(); // no additional call should have been made expect(keyQuerySpy).toHaveBeenCalledTimes(1); // The loop should resume after the retry_after_ms - jest.advanceTimersByTime(BACKOFF_TIME + 100); + vi.advanceTimersByTime(BACKOFF_TIME + 100); await Promise.resolve(); await keyImported; @@ -598,16 +591,14 @@ describe("PerSessionKeyBackupDownloader", () => { return; }); - fetchMock.get(`express:/_matrix/client/v3/room_keys/keys/:roomId/:sessionId`, mockCipherKey, { - overwriteRoutes: true, - }); + fetchMock.get(`express:/_matrix/client/v3/room_keys/keys/:roomId/:sessionId`, mockCipherKey); // @ts-ignore access to private function - const keyQuerySpy: SpyInstance = jest.spyOn(downloader, "queryKeyBackup"); + const keyQuerySpy: SpyInstance = vi.spyOn(downloader, "queryKeyBackup"); downloader.onDecryptionKeyMissingError("!roomA", "sessionA0"); downloader.onDecryptionKeyMissingError("!roomA", "sessionA1"); - await jest.runAllTimersAsync(); + await vi.runAllTimersAsync(); await keyImported.promise; diff --git a/spec/unit/rust-crypto/RoomEncryptor.spec.ts b/spec/unit/rust-crypto/RoomEncryptor.spec.ts index 7e20a9504c6..6fa4eba7973 100644 --- a/spec/unit/rust-crypto/RoomEncryptor.spec.ts +++ b/spec/unit/rust-crypto/RoomEncryptor.spec.ts @@ -16,6 +16,8 @@ * / */ +import { beforeEach, describe, expect, it, vi, Mocked } from "vitest"; + import { CollectStrategy, Curve25519PublicKey, @@ -24,7 +26,6 @@ import { IdentityKeys, OlmMachine, } from "@matrix-org/matrix-sdk-crypto-wasm"; -import { Mocked } from "jest-mock"; import { HistoryVisibility, MatrixEvent, Room, RoomMember } from "../../../src"; import { RoomEncryptor, toRustHistoryVisibility } from "../../../src/rust-crypto/RoomEncryptor"; @@ -62,13 +63,13 @@ describe("RoomEncryptor", () => { function createMockEvent(text: string): Mocked { return { - getTxnId: jest.fn().mockReturnValue(""), - getType: jest.fn().mockReturnValue("m.room.message"), - getContent: jest.fn().mockReturnValue({ + getTxnId: vi.fn().mockReturnValue(""), + getType: vi.fn().mockReturnValue("m.room.message"), + getContent: vi.fn().mockReturnValue({ body: text, msgtype: "m.text", }), - makeEncrypted: jest.fn().mockReturnValue(undefined), + makeEncrypted: vi.fn().mockReturnValue(undefined), } as unknown as Mocked; } @@ -76,32 +77,32 @@ describe("RoomEncryptor", () => { mockOlmMachine = { identityKeys: { curve25519: { - toBase64: jest.fn().mockReturnValue("curve25519"), + toBase64: vi.fn().mockReturnValue("curve25519"), } as unknown as Curve25519PublicKey, ed25519: { - toBase64: jest.fn().mockReturnValue("ed25519"), + toBase64: vi.fn().mockReturnValue("ed25519"), } as unknown as Ed25519PublicKey, } as unknown as Mocked, - shareRoomKey: jest.fn(), - updateTrackedUsers: jest.fn().mockResolvedValue(undefined), - encryptRoomEvent: jest.fn().mockResolvedValue("{}"), + shareRoomKey: vi.fn(), + updateTrackedUsers: vi.fn().mockResolvedValue(undefined), + encryptRoomEvent: vi.fn().mockResolvedValue("{}"), } as unknown as Mocked; mockKeyClaimManager = { - ensureSessionsForUsers: jest.fn(), + ensureSessionsForUsers: vi.fn(), } as unknown as Mocked; mockOutgoingRequestManager = { - doProcessOutgoingRequests: jest.fn().mockResolvedValue(undefined), + doProcessOutgoingRequests: vi.fn().mockResolvedValue(undefined), } as unknown as Mocked; mockRoom = { roomId: "!foo:example.org", - getJoinedMembers: jest.fn().mockReturnValue([mockRoomMember]), - getEncryptionTargetMembers: jest.fn().mockReturnValue([mockRoomMember]), - shouldEncryptForInvitedMembers: jest.fn().mockReturnValue(true), - getHistoryVisibility: jest.fn().mockReturnValue(HistoryVisibility.Invited), - getBlacklistUnverifiedDevices: jest.fn().mockReturnValue(null), + getJoinedMembers: vi.fn().mockReturnValue([mockRoomMember]), + getEncryptionTargetMembers: vi.fn().mockReturnValue([mockRoomMember]), + shouldEncryptForInvitedMembers: vi.fn().mockReturnValue(true), + getHistoryVisibility: vi.fn().mockReturnValue(HistoryVisibility.Invited), + getBlacklistUnverifiedDevices: vi.fn().mockReturnValue(null), } as unknown as Mocked; roomEncryptor = new RoomEncryptor( diff --git a/spec/unit/rust-crypto/backup.spec.ts b/spec/unit/rust-crypto/backup.spec.ts index ab5b740ee64..1810f59f260 100644 --- a/spec/unit/rust-crypto/backup.spec.ts +++ b/spec/unit/rust-crypto/backup.spec.ts @@ -1,5 +1,5 @@ -import { Mocked } from "jest-mock"; -import fetchMock from "fetch-mock-jest"; +import { afterEach, beforeEach, describe, expect, it, vi, Mocked } from "vitest"; +import fetchMock from "@fetch-mock/vitest"; import * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-wasm"; import { HttpApiEvent, HttpApiEventHandlerMap, MatrixHttpApi, TypedEventEmitter } from "../../../src"; @@ -42,25 +42,25 @@ describe("Upload keys to backup", () => { } beforeEach(async () => { - jest.useFakeTimers(); + vi.useFakeTimers(); idGenerator = 0; mockOlmMachine = { - getBackupKeys: jest.fn().mockResolvedValue({ + getBackupKeys: vi.fn().mockResolvedValue({ backupVersion: TestData.SIGNED_BACKUP_DATA.version!, decryptionKey: RustSdkCryptoJs.BackupDecryptionKey.fromBase64(TestData.BACKUP_DECRYPTION_KEY_BASE64), } as unknown as RustSdkCryptoJs.BackupKeys), - backupRoomKeys: jest.fn(), - isBackupEnabled: jest.fn().mockResolvedValue(true), - enableBackupV1: jest.fn(), - verifyBackup: jest.fn().mockResolvedValue({ - trusted: jest.fn().mockResolvedValue(true), + backupRoomKeys: vi.fn(), + isBackupEnabled: vi.fn().mockResolvedValue(true), + enableBackupV1: vi.fn(), + verifyBackup: vi.fn().mockResolvedValue({ + trusted: vi.fn().mockResolvedValue(true), } as unknown as RustSdkCryptoJs.SignatureVerification), - roomKeyCounts: jest.fn(), + roomKeyCounts: vi.fn(), } as unknown as Mocked; outgoingRequestProcessor = { - makeOutgoingRequest: jest.fn(), + makeOutgoingRequest: vi.fn(), } as unknown as Mocked; rustBackupManager = new RustBackupManager(mockOlmMachine, httpAPi, outgoingRequestProcessor); @@ -69,9 +69,8 @@ describe("Upload keys to backup", () => { }); afterEach(() => { - fetchMock.reset(); - jest.useRealTimers(); - jest.resetAllMocks(); + vi.useRealTimers(); + vi.resetAllMocks(); }); it("Should call expensive roomKeyCounts only once per loop", async () => { @@ -104,7 +103,7 @@ describe("Upload keys to backup", () => { }); await rustBackupManager.checkKeyBackupAndEnable(false); - await jest.runAllTimersAsync(); + await vi.runAllTimersAsync(); await zeroRemainingWasEmitted; @@ -133,7 +132,7 @@ describe("Upload keys to backup", () => { mockOlmMachine.backupRoomKeys.mockResolvedValueOnce(mockBackupRequest(2)).mockResolvedValue(null); await rustBackupManager.checkKeyBackupAndEnable(false); - await jest.runAllTimersAsync(); + await vi.runAllTimersAsync(); await zeroRemainingWasEmitted; diff --git a/spec/unit/rust-crypto/device-converter.spec.ts b/spec/unit/rust-crypto/device-converter.spec.ts index 5fffb53229d..850cbb179ab 100644 --- a/spec/unit/rust-crypto/device-converter.spec.ts +++ b/spec/unit/rust-crypto/device-converter.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { describe, expect, it } from "vitest"; + import { DeviceKeys, DeviceVerification } from "../../../src"; import { downloadDeviceToJsDevice } from "../../../src/rust-crypto/device-converter"; diff --git a/spec/unit/rust-crypto/rust-crypto.spec.ts b/spec/unit/rust-crypto/rust-crypto.spec.ts index 52eb8bfe907..c9631531197 100644 --- a/spec/unit/rust-crypto/rust-crypto.spec.ts +++ b/spec/unit/rust-crypto/rust-crypto.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi, Mocked } from "vitest"; + import * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-wasm"; import { BaseMigrationData, @@ -24,8 +26,7 @@ import { PickledSession, StoreHandle, } from "@matrix-org/matrix-sdk-crypto-wasm"; -import { mocked, Mocked } from "jest-mock"; -import fetchMock from "fetch-mock-jest"; +import fetchMock from "@fetch-mock/vitest"; import { RustCrypto } from "../../../src/rust-crypto/rust-crypto"; import { initRustCrypto } from "../../../src/rust-crypto"; @@ -77,6 +78,7 @@ import encryptAESSecretStorageItem from "../../../src/utils/encryptAESSecretStor import { CryptoStore, SecretStorePrivateKeys } from "../../../src/crypto/store/base"; import { CryptoEvent } from "../../../src/crypto-api/index.ts"; import { RustBackupManager } from "../../../src/rust-crypto/backup.ts"; +import { mocked } from "../../test-utils"; const TEST_USER = "@alice:example.com"; const TEST_DEVICE_ID = "TEST_DEVICE"; @@ -88,35 +90,34 @@ beforeAll(async () => { }, 15000); afterEach(() => { - fetchMock.reset(); - jest.restoreAllMocks(); + vi.restoreAllMocks(); }); describe("initRustCrypto", () => { function makeTestOlmMachine(): Mocked { return { - registerRoomKeyUpdatedCallback: jest.fn(), - registerUserIdentityUpdatedCallback: jest.fn(), - getSecretsFromInbox: jest.fn().mockResolvedValue([]), - deleteSecretsFromInbox: jest.fn(), - registerReceiveSecretCallback: jest.fn(), - registerDevicesUpdatedCallback: jest.fn(), - registerRoomKeysWithheldCallback: jest.fn(), - outgoingRequests: jest.fn(), - isBackupEnabled: jest.fn().mockResolvedValue(false), - verifyBackup: jest.fn().mockResolvedValue({ trusted: jest.fn().mockReturnValue(false) }), - getBackupKeys: jest.fn(), - getIdentity: jest.fn().mockResolvedValue(null), - trackedUsers: jest.fn(), + registerRoomKeyUpdatedCallback: vi.fn(), + registerUserIdentityUpdatedCallback: vi.fn(), + getSecretsFromInbox: vi.fn().mockResolvedValue([]), + deleteSecretsFromInbox: vi.fn(), + registerReceiveSecretCallback: vi.fn(), + registerDevicesUpdatedCallback: vi.fn(), + registerRoomKeysWithheldCallback: vi.fn(), + outgoingRequests: vi.fn(), + isBackupEnabled: vi.fn().mockResolvedValue(false), + verifyBackup: vi.fn().mockResolvedValue({ trusted: vi.fn().mockReturnValue(false) }), + getBackupKeys: vi.fn(), + getIdentity: vi.fn().mockResolvedValue(null), + trackedUsers: vi.fn(), } as unknown as Mocked; } it("passes through the store params (passphrase)", async () => { - const mockStore = { free: jest.fn() } as unknown as StoreHandle; - jest.spyOn(StoreHandle, "open").mockResolvedValue(mockStore); + const mockStore = { free: vi.fn() } as unknown as StoreHandle; + vi.spyOn(StoreHandle, "open").mockResolvedValue(mockStore); const testOlmMachine = makeTestOlmMachine(); - jest.spyOn(OlmMachine, "initFromStore").mockResolvedValue(testOlmMachine); + vi.spyOn(OlmMachine, "initFromStore").mockResolvedValue(testOlmMachine); await initRustCrypto({ logger, @@ -134,11 +135,11 @@ describe("initRustCrypto", () => { }); it("passes through the store params (key)", async () => { - const mockStore = { free: jest.fn() } as unknown as StoreHandle; - jest.spyOn(StoreHandle, "openWithKey").mockResolvedValue(mockStore); + const mockStore = { free: vi.fn() } as unknown as StoreHandle; + vi.spyOn(StoreHandle, "openWithKey").mockResolvedValue(mockStore); const testOlmMachine = makeTestOlmMachine(); - jest.spyOn(OlmMachine, "initFromStore").mockResolvedValue(testOlmMachine); + vi.spyOn(OlmMachine, "initFromStore").mockResolvedValue(testOlmMachine); const storeKey = new Uint8Array(32); await initRustCrypto({ @@ -157,11 +158,11 @@ describe("initRustCrypto", () => { }); it("suppresses the storePassphrase and storeKey if storePrefix is unset", async () => { - const mockStore = { free: jest.fn() } as unknown as StoreHandle; - jest.spyOn(StoreHandle, "open").mockResolvedValue(mockStore); + const mockStore = { free: vi.fn() } as unknown as StoreHandle; + vi.spyOn(StoreHandle, "open").mockResolvedValue(mockStore); const testOlmMachine = makeTestOlmMachine(); - jest.spyOn(OlmMachine, "initFromStore").mockResolvedValue(testOlmMachine); + vi.spyOn(OlmMachine, "initFromStore").mockResolvedValue(testOlmMachine); await initRustCrypto({ logger, @@ -180,11 +181,11 @@ describe("initRustCrypto", () => { }); it("Should get secrets from inbox on start", async () => { - const mockStore = { free: jest.fn() } as unknown as StoreHandle; - jest.spyOn(StoreHandle, "open").mockResolvedValue(mockStore); + const mockStore = { free: vi.fn() } as unknown as StoreHandle; + vi.spyOn(StoreHandle, "open").mockResolvedValue(mockStore); const testOlmMachine = makeTestOlmMachine(); - jest.spyOn(OlmMachine, "initFromStore").mockResolvedValue(testOlmMachine); + vi.spyOn(OlmMachine, "initFromStore").mockResolvedValue(testOlmMachine); await initRustCrypto({ logger, @@ -205,16 +206,16 @@ describe("initRustCrypto", () => { beforeEach(() => { // Stub out a bunch of stuff in the Rust library - mockStore = { free: jest.fn() } as unknown as StoreHandle; - jest.spyOn(StoreHandle, "open").mockResolvedValue(mockStore); + mockStore = { free: vi.fn() } as unknown as StoreHandle; + vi.spyOn(StoreHandle, "open").mockResolvedValue(mockStore); - jest.spyOn(Migration, "migrateBaseData").mockResolvedValue(undefined); - jest.spyOn(Migration, "migrateOlmSessions").mockResolvedValue(undefined); - jest.spyOn(Migration, "migrateMegolmSessions").mockResolvedValue(undefined); + vi.spyOn(Migration, "migrateBaseData").mockResolvedValue(undefined); + vi.spyOn(Migration, "migrateOlmSessions").mockResolvedValue(undefined); + vi.spyOn(Migration, "migrateMegolmSessions").mockResolvedValue(undefined); const testOlmMachine = makeTestOlmMachine(); testOlmMachine.trackedUsers.mockResolvedValue([]); - jest.spyOn(OlmMachine, "initFromStore").mockResolvedValue(testOlmMachine); + vi.spyOn(OlmMachine, "initFromStore").mockResolvedValue(testOlmMachine); }); it("migrates data from a legacy crypto store", async () => { @@ -249,7 +250,7 @@ describe("initRustCrypto", () => { publicKeyBase64: "backup_key_public", }, }; - jest.spyOn(RustSdkCryptoJs.BackupDecryptionKey, "fromBase64").mockReturnValue(mockBackupDecryptionKey); + vi.spyOn(RustSdkCryptoJs.BackupDecryptionKey, "fromBase64").mockReturnValue(mockBackupDecryptionKey); function legacyMigrationProgressListener(progress: number, total: number): void { logger.log(`migrated ${progress} of ${total}`); @@ -611,7 +612,7 @@ describe("RustCrypto", () => { sender: testData.TEST_USER_ID, }; - const onEvent = jest.fn(); + const onEvent = vi.fn(); rustCrypto.on(CryptoEvent.VerificationRequestReceived, onEvent); await rustCrypto.preprocessToDeviceMessages([toDeviceEvent]); expect(onEvent).toHaveBeenCalledTimes(1); @@ -629,8 +630,8 @@ describe("RustCrypto", () => { describe("getCrossSigningStatus", () => { it("returns sensible values on a default client", async () => { const secretStorage = { - isStored: jest.fn().mockResolvedValue(null), - getDefaultKeyId: jest.fn().mockResolvedValue("key"), + isStored: vi.fn().mockResolvedValue(null), + getDefaultKeyId: vi.fn().mockResolvedValue("key"), } as unknown as Mocked; const rustCrypto = await makeTestRustCrypto(undefined, undefined, undefined, secretStorage); @@ -650,8 +651,8 @@ describe("RustCrypto", () => { it("throws if `stop` is called mid-call", async () => { const secretStorage = { - isStored: jest.fn().mockResolvedValue(null), - getDefaultKeyId: jest.fn().mockResolvedValue(null), + isStored: vi.fn().mockResolvedValue(null), + getDefaultKeyId: vi.fn().mockResolvedValue(null), } as unknown as Mocked; const rustCrypto = await makeTestRustCrypto(undefined, undefined, undefined, secretStorage); @@ -669,7 +670,7 @@ describe("RustCrypto", () => { it("bootstrapCrossSigning delegates to CrossSigningIdentity", async () => { const rustCrypto = await makeTestRustCrypto(); const mockCrossSigningIdentity = { - bootstrapCrossSigning: jest.fn().mockResolvedValue(undefined), + bootstrapCrossSigning: vi.fn().mockResolvedValue(undefined), }; // @ts-ignore private property rustCrypto.crossSigningIdentity = mockCrossSigningIdentity; @@ -686,7 +687,7 @@ describe("RustCrypto", () => { const secretStorage = new ServerSideSecretStorageImpl(new DummyAccountDataClient(), secretStorageCallbacks); const outgoingRequestProcessor = { - makeOutgoingRequest: jest.fn(), + makeOutgoingRequest: vi.fn(), } as unknown as Mocked; const rustCrypto = await makeTestRustCrypto( @@ -704,7 +705,7 @@ describe("RustCrypto", () => { return null; }; (rustCrypto["crossSigningIdentity"] as any)["outgoingRequestProcessor"] = outgoingRequestProcessor; - const resetKeyBackup = (rustCrypto["resetKeyBackup"] = jest.fn()); + const resetKeyBackup = (rustCrypto["resetKeyBackup"] = vi.fn()); async function createSecretStorageKey() { return { @@ -745,7 +746,7 @@ describe("RustCrypto", () => { let backupAlg: string; const fetchMock = { - authedRequest: jest.fn().mockImplementation((method, path, query, body) => { + authedRequest: vi.fn().mockImplementation((method, path, query, body) => { if (path === "/room_keys/version") { if (method === "POST") { backupAuthData = body["auth_data"]; @@ -783,7 +784,7 @@ describe("RustCrypto", () => { await rustCrypto.resetKeyBackup(); - const storeSpy = jest.spyOn(secretStorage, "store"); + const storeSpy = vi.spyOn(secretStorage, "store"); await rustCrypto.bootstrapSecretStorage({ createSecretStorageKey, @@ -796,23 +797,23 @@ describe("RustCrypto", () => { it("bootstrapSecretStorage doesn't try to save megolm backup key not in cache", async () => { const mockOlmMachine = { - isBackupEnabled: jest.fn().mockResolvedValue(false), - sign: jest.fn().mockResolvedValue({ - asJSON: jest.fn().mockReturnValue("{}"), + isBackupEnabled: vi.fn().mockResolvedValue(false), + sign: vi.fn().mockResolvedValue({ + asJSON: vi.fn().mockReturnValue("{}"), }), - saveBackupDecryptionKey: jest.fn(), - crossSigningStatus: jest.fn().mockResolvedValue({ + saveBackupDecryptionKey: vi.fn(), + crossSigningStatus: vi.fn().mockResolvedValue({ hasMaster: true, hasSelfSigning: true, hasUserSigning: true, }), - exportCrossSigningKeys: jest.fn().mockResolvedValue({ + exportCrossSigningKeys: vi.fn().mockResolvedValue({ masterKey: "sosecret", userSigningKey: "secrets", self_signing_key: "ssshhh", }), - getBackupKeys: jest.fn().mockResolvedValue({}), - verifyBackup: jest.fn().mockResolvedValue({ trusted: jest.fn().mockReturnValue(false) }), + getBackupKeys: vi.fn().mockResolvedValue({}), + verifyBackup: vi.fn().mockResolvedValue({ trusted: vi.fn().mockReturnValue(false) }), } as unknown as OlmMachine; const rustCrypto = new RustCrypto( @@ -834,7 +835,7 @@ describe("RustCrypto", () => { await rustCrypto.resetKeyBackup(); - const storeSpy = jest.spyOn(secretStorage, "store"); + const storeSpy = vi.spyOn(secretStorage, "store"); await rustCrypto.bootstrapSecretStorage({ createSecretStorageKey, @@ -848,7 +849,7 @@ describe("RustCrypto", () => { it("isSecretStorageReady", async () => { const mockSecretStorage = { - getDefaultKeyId: jest.fn().mockResolvedValue(null), + getDefaultKeyId: vi.fn().mockResolvedValue(null), } as unknown as Mocked; const rustCrypto = await makeTestRustCrypto(undefined, undefined, undefined, mockSecretStorage); await expect(rustCrypto.isSecretStorageReady()).resolves.toBe(false); @@ -891,14 +892,14 @@ describe("RustCrypto", () => { // returns objects from outgoingRequestQueue outgoingRequestQueue = []; olmMachine = { - outgoingRequests: jest.fn().mockImplementation(() => { + outgoingRequests: vi.fn().mockImplementation(() => { return Promise.resolve(outgoingRequestQueue.shift() ?? []); }), - close: jest.fn(), + close: vi.fn(), } as unknown as Mocked; outgoingRequestProcessor = { - makeOutgoingRequest: jest.fn(), + makeOutgoingRequest: vi.fn(), } as unknown as Mocked; const outgoingRequestsManager = new OutgoingRequestsManager(logger, olmMachine, outgoingRequestProcessor); @@ -972,7 +973,7 @@ describe("RustCrypto", () => { let keysUploadCount = 0; let deviceKeys: object; let deviceKeysAbsent = false; - outgoingRequestProcessor.makeOutgoingRequest = jest.fn(async (request, uiaCallback?) => { + outgoingRequestProcessor.makeOutgoingRequest = vi.fn(async (request, uiaCallback?) => { let resp: any = {}; if (request instanceof RustSdkCryptoJs.KeysUploadRequest) { if (keysUploadCount == 0) { @@ -1043,7 +1044,7 @@ describe("RustCrypto", () => { beforeEach(() => { olmMachine = { - getRoomEventEncryptionInfo: jest.fn(), + getRoomEventEncryptionInfo: vi.fn(), } as unknown as Mocked; rustCrypto = new RustCrypto( logger, @@ -1121,7 +1122,7 @@ describe("RustCrypto", () => { [RustSdkCryptoJs.ShieldColor.Red, EventShieldColour.RED], ])("gets the right shield color (%i)", async (rustShield, expectedShield) => { const mockEncryptionInfo = { - shieldState: jest.fn().mockReturnValue({ color: rustShield, message: undefined }), + shieldState: vi.fn().mockReturnValue({ color: rustShield, message: undefined }), } as unknown as RustSdkCryptoJs.EncryptionInfo; olmMachine.getRoomEventEncryptionInfo.mockResolvedValue(mockEncryptionInfo); @@ -1161,10 +1162,10 @@ describe("RustCrypto", () => { ], ])("gets the right shield reason (%s)", async (rustReason, rustCode, expectedReason) => { // suppress the warning from the unknown shield reason - jest.spyOn(console, "warn").mockImplementation(() => {}); + vi.spyOn(console, "warn").mockImplementation(() => {}); const mockEncryptionInfo = { - shieldState: jest + shieldState: vi .fn() .mockReturnValue({ color: RustSdkCryptoJs.ShieldColor.None, code: rustCode, message: rustReason }), } as unknown as RustSdkCryptoJs.EncryptionInfo; @@ -1256,7 +1257,7 @@ describe("RustCrypto", () => { beforeEach(() => { olmMachine = { - getDevice: jest.fn(), + getDevice: vi.fn(), } as unknown as Mocked; rustCrypto = new RustCrypto( logger, @@ -1271,10 +1272,10 @@ describe("RustCrypto", () => { it("should call getDevice", async () => { olmMachine.getDevice.mockResolvedValue({ - free: jest.fn(), - isCrossSigningTrusted: jest.fn().mockReturnValue(false), - isLocallyTrusted: jest.fn().mockReturnValue(false), - isCrossSignedByOwner: jest.fn().mockReturnValue(false), + free: vi.fn(), + isCrossSigningTrusted: vi.fn().mockReturnValue(false), + isLocallyTrusted: vi.fn().mockReturnValue(false), + isCrossSignedByOwner: vi.fn().mockReturnValue(false), } as unknown as RustSdkCryptoJs.Device); const res = await rustCrypto.getDeviceVerificationStatus("@user:domain", "device"); expect(olmMachine.getDevice.mock.calls[0][0].toString()).toEqual("@user:domain"); @@ -1384,7 +1385,7 @@ describe("RustCrypto", () => { it("should wait for a keys/query before returning devices", async () => { // We want to use fake timers, but the wasm bindings of matrix-sdk-crypto rely on a working `queueMicrotask`. - jest.useFakeTimers({ doNotFake: ["queueMicrotask"] }); + vi.useFakeTimers({ doNotFake: ["queueMicrotask"] }); fetchMock.post("path:/_matrix/client/v3/keys/upload", { one_time_key_counts: {} }); fetchMock.post("path:/_matrix/client/v3/keys/query", { @@ -1410,7 +1411,7 @@ describe("RustCrypto", () => { }); it("should emit events on device changes", async () => { - jest.useFakeTimers({ doNotFake: ["queueMicrotask"] }); + vi.useFakeTimers({ doNotFake: ["queueMicrotask"] }); fetchMock.post("path:/_matrix/client/v3/keys/upload", { one_time_key_counts: {} }); fetchMock.post("path:/_matrix/client/v3/keys/query", { @@ -1422,9 +1423,9 @@ describe("RustCrypto", () => { }); const rustCrypto = await makeTestRustCrypto(makeMatrixHttpApi(), testData.TEST_USER_ID); - const willUpdateCallback = jest.fn(); + const willUpdateCallback = vi.fn(); rustCrypto.on(CryptoEvent.WillUpdateDevices, willUpdateCallback); - const devicesUpdatedCallback = jest.fn(); + const devicesUpdatedCallback = vi.fn(); rustCrypto.on(CryptoEvent.DevicesUpdated, devicesUpdatedCallback); rustCrypto.onSyncCompleted({}); @@ -1499,7 +1500,7 @@ describe("RustCrypto", () => { beforeEach(() => { olmMachine = { - getIdentity: jest.fn(), + getIdentity: vi.fn(), } as unknown as Mocked; rustCrypto = new RustCrypto( logger, @@ -1522,9 +1523,9 @@ describe("RustCrypto", () => { it("returns a verified UserVerificationStatus when the UserIdentity is verified", async () => { olmMachine.getIdentity.mockResolvedValue({ - free: jest.fn(), - isVerified: jest.fn().mockReturnValue(true), - wasPreviouslyVerified: jest.fn().mockReturnValue(true), + free: vi.fn(), + isVerified: vi.fn().mockReturnValue(true), + wasPreviouslyVerified: vi.fn().mockReturnValue(true), }); const userVerificationStatus = await rustCrypto.getUserVerificationStatus(testData.TEST_USER_ID); @@ -1564,7 +1565,7 @@ describe("RustCrypto", () => { }); it("throws an error for our own user", async () => { - jest.useRealTimers(); + vi.useRealTimers(); const e2eKeyReceiver = new E2EKeyReceiver("http://server"); const e2eKeyResponder = new E2EKeyResponder("http://server"); e2eKeyResponder.addKeyReceiver(TEST_USER, e2eKeyReceiver); @@ -1586,7 +1587,7 @@ describe("RustCrypto", () => { describe("withdraw verification", () => { function createTestSetup(): { olmMachine: Mocked; rustCrypto: RustCrypto } { const olmMachine = { - getIdentity: jest.fn(), + getIdentity: vi.fn(), } as unknown as Mocked; const rustCrypto = new RustCrypto( logger, @@ -1610,7 +1611,7 @@ describe("RustCrypto", () => { it("Calls withdraw for other identity", async () => { const { olmMachine, rustCrypto } = createTestSetup(); const identity = { - withdrawVerification: jest.fn(), + withdrawVerification: vi.fn(), } as unknown as Mocked; olmMachine.getIdentity.mockResolvedValue(identity); @@ -1623,7 +1624,7 @@ describe("RustCrypto", () => { it("Calls withdraw for own identity", async () => { const { olmMachine, rustCrypto } = createTestSetup(); const ownIdentity = { - withdrawVerification: jest.fn(), + withdrawVerification: vi.fn(), } as unknown as Mocked; olmMachine.getIdentity.mockResolvedValue(ownIdentity); @@ -1638,7 +1639,7 @@ describe("RustCrypto", () => { it("is started when rust crypto is created", async () => { // `RustCrypto.checkKeyBackupAndEnable` async call is made in background in the RustCrypto constructor. // We don't have an instance of the rust crypto yet, we spy directly in the prototype. - const spyCheckKeyBackupAndEnable = jest + const spyCheckKeyBackupAndEnable = vi .spyOn(RustCrypto.prototype, "checkKeyBackupAndEnable") .mockResolvedValue({} as KeyBackupCheck); @@ -1652,12 +1653,12 @@ describe("RustCrypto", () => { fetchMock.get("path:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA); const olmMachine = { - getIdentity: jest.fn(), + getIdentity: vi.fn(), // Force the backup to be trusted by the olmMachine - verifyBackup: jest.fn().mockResolvedValue({ trusted: jest.fn().mockReturnValue(true) }), - isBackupEnabled: jest.fn().mockReturnValue(true), - getBackupKeys: jest.fn(), - enableBackupV1: jest.fn(), + verifyBackup: vi.fn().mockResolvedValue({ trusted: vi.fn().mockReturnValue(true) }), + isBackupEnabled: vi.fn().mockReturnValue(true), + getBackupKeys: vi.fn(), + enableBackupV1: vi.fn(), } as unknown as Mocked; const rustCrypto = new RustCrypto( @@ -1734,7 +1735,7 @@ describe("RustCrypto", () => { const backup = Array.from(testData.MEGOLM_SESSION_DATA_ARRAY); // in addition to correct keys, we restore an invalid key backup.push({ room_id: "!roomid", session_id: "sessionid" } as IMegolmSessionData); - const progressCallback = jest.fn(); + const progressCallback = vi.fn(); await rustCrypto.importBackedUpRoomKeys(backup, backupVersion, { progressCallback }); expect(progressCallback).toHaveBeenCalledWith({ total: 3, @@ -1768,7 +1769,6 @@ describe("RustCrypto", () => { describe("device dehydration", () => { it("should detect if dehydration is supported", async () => { const rustCrypto = await makeTestRustCrypto(makeMatrixHttpApi()); - fetchMock.config.overwriteRoutes = true; fetchMock.get("path:/_matrix/client/unstable/org.matrix.msc3814.v1/dehydrated_device", { status: 404, body: { @@ -1793,8 +1793,6 @@ describe("RustCrypto", () => { }); it("should load the dehydration key from SSSS if available", async () => { - fetchMock.config.overwriteRoutes = true; - const secretStorageCallbacks = { getSecretStorageKey: async (keys: any, name: string) => { return [[...Object.keys(keys.keys)][0], new Uint8Array(32)]; @@ -1903,7 +1901,7 @@ describe("RustCrypto", () => { // Function that is called when `GET /dehydrated_device` is called // (i.e. when we try to rehydrate a device) - const getDehydratedDeviceMock = jest.fn(() => { + const getDehydratedDeviceMock = vi.fn(() => { if (dehydratedDeviceInfo) { return { status: 200, @@ -1921,7 +1919,7 @@ describe("RustCrypto", () => { }); // Function that is called when `PUT /dehydrated_device` is called // (i.e. when we create a new dehydrated device) - const putDehydratedDeviceMock = jest.fn((path, opts) => { + const putDehydratedDeviceMock = vi.fn((path, opts) => { const content = JSON.parse(opts.body as string); dehydratedDeviceInfo = { device_id: content.device_id, @@ -2131,7 +2129,7 @@ describe("RustCrypto", () => { new RustSdkCryptoJs.UserId(testData.TEST_USER_ID), new RustSdkCryptoJs.DeviceId(testData.TEST_DEVICE_ID), ); - jest.spyOn(OlmMachine, "initFromStore").mockResolvedValue(testOlmMachine); + vi.spyOn(OlmMachine, "initFromStore").mockResolvedValue(testOlmMachine); rustCrypto = await makeTestRustCrypto(); expect(OlmMachine.initFromStore).toHaveBeenCalled(); }); @@ -2143,8 +2141,8 @@ describe("RustCrypto", () => { const payload = { hello: "world" }; it("returns empty batch if devices not known", async () => { - const getMissingSessions = jest.spyOn(testOlmMachine, "getMissingSessions"); - const getDevice = jest.spyOn(testOlmMachine, "getDevice"); + const getMissingSessions = vi.spyOn(testOlmMachine, "getMissingSessions"); + const getDevice = vi.spyOn(testOlmMachine, "getDevice"); const batch = await rustCrypto.encryptToDeviceMessages( "m.test.type", [ @@ -2244,11 +2242,11 @@ describe("RustCrypto", () => { let secretStorage: ServerSideSecretStorage; beforeEach(() => { secretStorage = { - setDefaultKeyId: jest.fn(), - hasKey: jest.fn().mockResolvedValue(false), - getKey: jest.fn().mockResolvedValue(null), - store: jest.fn(), - getDefaultKeyId: jest.fn().mockResolvedValue("defaultKeyId"), + setDefaultKeyId: vi.fn(), + hasKey: vi.fn().mockResolvedValue(false), + getKey: vi.fn().mockResolvedValue(null), + store: vi.fn(), + getDefaultKeyId: vi.fn().mockResolvedValue("defaultKeyId"), } as unknown as ServerSideSecretStorage; fetchMock.post("path:/_matrix/client/v3/keys/upload", { one_time_key_counts: {} }); @@ -2275,7 +2273,7 @@ describe("RustCrypto", () => { }); // We consider the key backup as trusted - jest.spyOn(RustBackupManager.prototype, "isKeyBackupTrusted").mockResolvedValue({ + vi.spyOn(RustBackupManager.prototype, "isKeyBackupTrusted").mockResolvedValue({ trusted: true, matchesDecryptionKey: true, }); @@ -2284,7 +2282,7 @@ describe("RustCrypto", () => { // We have a key backup expect(await rustCrypto.getActiveSessionBackupVersion()).not.toBeNull(); - const authUploadDeviceSigningKeys = jest.fn(); + const authUploadDeviceSigningKeys = vi.fn(); await rustCrypto.resetEncryption(authUploadDeviceSigningKeys); // The secrets in 4S should be deleted diff --git a/spec/unit/rust-crypto/secret-storage.spec.ts b/spec/unit/rust-crypto/secret-storage.spec.ts index e54461a26e4..b70c09722e2 100644 --- a/spec/unit/rust-crypto/secret-storage.spec.ts +++ b/spec/unit/rust-crypto/secret-storage.spec.ts @@ -14,10 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { - secretStorageCanAccessSecrets, - secretStorageContainsCrossSigningKeys, -} from "../../../src/rust-crypto/secret-storage"; +import { describe, expect, it, vi } from "vitest"; + +import { secretStorageCanAccessSecrets, secretStorageContainsCrossSigningKeys } from "../../../src/rust-crypto/secret-storage"; import { ServerSideSecretStorage } from "../../../src/secret-storage"; import { SecretInfo } from "../../../src/secret-storage.ts"; @@ -36,8 +35,8 @@ describe("secret-storage", () => { describe("secretStorageContainsCrossSigningKeys", () => { it("should return false when the master cross-signing key is not stored in secret storage", async () => { const secretStorage = { - isStored: jest.fn().mockReturnValue(false), - getDefaultKeyId: jest.fn().mockResolvedValue("SFQ3TbqGOdaaRVfxHtNkn0tvhx0rVj9S"), + isStored: vi.fn().mockReturnValue(false), + getDefaultKeyId: vi.fn().mockResolvedValue("SFQ3TbqGOdaaRVfxHtNkn0tvhx0rVj9S"), } as unknown as ServerSideSecretStorage; const result = await secretStorageContainsCrossSigningKeys(secretStorage); @@ -51,7 +50,7 @@ describe("secret-storage", () => { if (type === "m.cross_signing.master") return { secretStorageKey: {} }; else return { secretStorageKey2: {} }; }, - getDefaultKeyId: jest.fn().mockResolvedValue("SFQ3TbqGOdaaRVfxHtNkn0tvhx0rVj9S"), + getDefaultKeyId: vi.fn().mockResolvedValue("SFQ3TbqGOdaaRVfxHtNkn0tvhx0rVj9S"), } as unknown as ServerSideSecretStorage; const result = await secretStorageContainsCrossSigningKeys(secretStorage); @@ -68,7 +67,7 @@ describe("secret-storage", () => { return { secretStorageKey2: {} }; } }, - getDefaultKeyId: jest.fn().mockResolvedValue("secretStorageKey"), + getDefaultKeyId: vi.fn().mockResolvedValue("secretStorageKey"), } as unknown as ServerSideSecretStorage; const result = await secretStorageContainsCrossSigningKeys(secretStorage); @@ -77,8 +76,8 @@ describe("secret-storage", () => { it("should return true when master, user signing and self signing keys are all encrypted with default key", async () => { const secretStorage = { - isStored: jest.fn().mockReturnValue({ secretStorageKey: {} }), - getDefaultKeyId: jest.fn().mockResolvedValue("secretStorageKey"), + isStored: vi.fn().mockReturnValue({ secretStorageKey: {} }), + getDefaultKeyId: vi.fn().mockResolvedValue("secretStorageKey"), } as unknown as ServerSideSecretStorage; const result = await secretStorageContainsCrossSigningKeys(secretStorage); @@ -87,8 +86,8 @@ describe("secret-storage", () => { it("should return false when master, user signing and self signing keys are all encrypted with a non-default key", async () => { const secretStorage = { - isStored: jest.fn().mockResolvedValue({ defaultKey: {} }), - getDefaultKeyId: jest.fn().mockResolvedValue("anotherCommonKey"), + isStored: vi.fn().mockResolvedValue({ defaultKey: {} }), + getDefaultKeyId: vi.fn().mockResolvedValue("anotherCommonKey"), } as unknown as ServerSideSecretStorage; const result = await secretStorageContainsCrossSigningKeys(secretStorage); @@ -98,7 +97,7 @@ describe("secret-storage", () => { it("Check canAccessSecrets", async () => { const secretStorage = { - isStored: jest.fn((secretName) => { + isStored: vi.fn((secretName) => { if (secretName == "secretA") { return { aaaa: {} }; } else if (secretName == "secretB") { @@ -113,7 +112,7 @@ describe("secret-storage", () => { return null; } }), - getDefaultKeyId: jest.fn().mockResolvedValue("aaaa"), + getDefaultKeyId: vi.fn().mockResolvedValue("aaaa"), } as unknown as ServerSideSecretStorage; expect(await secretStorageCanAccessSecrets(secretStorage, ["secretE"])).toStrictEqual(true); diff --git a/spec/unit/rust-crypto/verification.spec.ts b/spec/unit/rust-crypto/verification.spec.ts index 1579ee6a876..6a60ef62a72 100644 --- a/spec/unit/rust-crypto/verification.spec.ts +++ b/spec/unit/rust-crypto/verification.spec.ts @@ -14,8 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { beforeEach, describe, expect, it, vi, Mocked } from "vitest"; + import * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-wasm"; -import { Mocked } from "jest-mock"; import { isVerificationEvent, @@ -78,7 +79,7 @@ describe("VerificationRequest", () => { beforeEach(() => { inner = makeMockedInner(); - machine = { getDevice: jest.fn() } as unknown as Mocked; + machine = { getDevice: vi.fn() } as unknown as Mocked; request = makeTestRequest(inner, machine); }); @@ -95,7 +96,7 @@ describe("VerificationRequest", () => { }); it("raises an error if starting verification does not produce a verifier", async () => { - jest.spyOn(inner, "otherDeviceId", "get").mockReturnValue(new RustSdkCryptoJs.DeviceId("other_device")); + vi.spyOn(inner, "otherDeviceId", "get").mockReturnValue(new RustSdkCryptoJs.DeviceId("other_device")); machine.getDevice.mockResolvedValue({} as RustSdkCryptoJs.Device); await expect(request.startVerification("m.sas.v1")).rejects.toThrow( "Still no verifier after startSas() call", @@ -501,11 +502,11 @@ function makeTestRequest( /** Mock up a rust-side VerificationRequest */ function makeMockedInner(): Mocked { return { - registerChangesCallback: jest.fn(), - startSas: jest.fn(), - phase: jest.fn().mockReturnValue(RustSdkCryptoJs.VerificationRequestPhase.Created), - isPassive: jest.fn().mockReturnValue(false), - timeRemainingMillis: jest.fn(), + registerChangesCallback: vi.fn(), + startSas: vi.fn(), + phase: vi.fn().mockReturnValue(RustSdkCryptoJs.VerificationRequestPhase.Created), + isPassive: vi.fn().mockReturnValue(false), + timeRemainingMillis: vi.fn(), get otherDeviceId() { return undefined; }, diff --git a/spec/unit/scheduler.spec.ts b/spec/unit/scheduler.spec.ts index e37a3b38246..99b42f8d62b 100644 --- a/spec/unit/scheduler.spec.ts +++ b/spec/unit/scheduler.spec.ts @@ -1,6 +1,8 @@ // This file had a function whose name is all caps, which displeases eslint /* eslint new-cap: "off" */ +import { beforeEach, describe, expect, it, vi } from "vitest"; + import { defer, IDeferred } from "../../src/utils"; import { MatrixError } from "../../src/http-api"; import { MatrixScheduler } from "../../src/scheduler"; @@ -8,7 +10,7 @@ import * as utils from "../test-utils/test-utils"; import { MatrixEvent } from "../../src"; import { KnownMembership } from "../../src/@types/membership"; -jest.useFakeTimers(); +vi.useFakeTimers(); describe("MatrixScheduler", function () { let scheduler: MatrixScheduler>; @@ -59,12 +61,10 @@ describe("MatrixScheduler", function () { let yieldedA = false; scheduler.setProcessFunction(function (event) { if (yieldedA) { - // eslint-disable-next-line jest/no-conditional-expect expect(event).toEqual(eventB); return deferB.promise; } else { yieldedA = true; - // eslint-disable-next-line jest/no-conditional-expect expect(event).toEqual(eventA); return deferA.promise; } @@ -92,7 +92,6 @@ describe("MatrixScheduler", function () { scheduler.setProcessFunction(function (ev) { procCount += 1; if (procCount === 1) { - // eslint-disable-next-line jest/no-conditional-expect expect(ev).toEqual(eventA); return deferred.promise; } else if (procCount === 2) { @@ -111,7 +110,7 @@ describe("MatrixScheduler", function () { deferred.reject({}); await retryDefer.promise; expect(procCount).toEqual(1); - jest.advanceTimersByTime(waitTimeMs); + vi.advanceTimersByTime(waitTimeMs); await Promise.resolve(); expect(procCount).toEqual(2); }); @@ -133,11 +132,9 @@ describe("MatrixScheduler", function () { scheduler.setProcessFunction(function (ev) { procCount += 1; if (procCount === 1) { - // eslint-disable-next-line jest/no-conditional-expect expect(ev).toEqual(eventA); return deferA.promise; } else if (procCount === 2) { - // eslint-disable-next-line jest/no-conditional-expect expect(ev).toEqual(eventB); return deferB.promise; } @@ -200,7 +197,7 @@ describe("MatrixScheduler", function () { setTimeout(function () { deferA.resolve({}); }, 1000); - jest.advanceTimersByTime(1000); + vi.advanceTimersByTime(1000); await allExpectedEventsSeenInOrderPromise; }); diff --git a/spec/unit/secret-storage.spec.ts b/spec/unit/secret-storage.spec.ts index 2e262c290d3..1d6af860a40 100644 --- a/spec/unit/secret-storage.spec.ts +++ b/spec/unit/secret-storage.spec.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { Mocked } from "jest-mock"; +import { beforeEach, describe, expect, it, vi, Mocked } from "vitest"; import { AccountDataClient, @@ -248,7 +248,7 @@ describe("ServerSideSecretStorageImpl", function () { beforeEach(() => { accountDataAdapter = mockAccountDataClient(); - const mockCallbacks = { getSecretStorageKey: jest.fn() } as Mocked; + const mockCallbacks = { getSecretStorageKey: vi.fn() } as Mocked; secretStorage = new ServerSideSecretStorageImpl(accountDataAdapter, mockCallbacks); }); @@ -267,7 +267,7 @@ describe("ServerSideSecretStorageImpl", function () { accountDataAdapter.getAccountDataFromServer.mockImplementation(mockGetAccountData); // suppress the expected warning on the console - jest.spyOn(console, "warn").mockImplementation(); + vi.spyOn(console, "warn").mockImplementation(); // now attempt the store await secretStorage.store("mysecret", "supersecret", ["keyid"]); @@ -377,8 +377,8 @@ describe("trimTrailingEquals", () => { function mockAccountDataClient(): Mocked { const eventEmitter = new TypedEventEmitter(); return { - getAccountDataFromServer: jest.fn().mockResolvedValue(null), - setAccountData: jest.fn().mockResolvedValue({}), + getAccountDataFromServer: vi.fn().mockResolvedValue(null), + setAccountData: vi.fn().mockResolvedValue({}), on: eventEmitter.on.bind(eventEmitter), off: eventEmitter.off.bind(eventEmitter), removeListener: eventEmitter.removeListener.bind(eventEmitter), diff --git a/spec/unit/stores/indexeddb-store-worker.spec.ts b/spec/unit/stores/indexeddb-store-worker.spec.ts index 536327d2e38..b1924f36a0f 100644 --- a/spec/unit/stores/indexeddb-store-worker.spec.ts +++ b/spec/unit/stores/indexeddb-store-worker.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { describe, expect, it, vi } from "vitest"; + import "fake-indexeddb/auto"; import { LocalIndexedDBStoreBackend } from "../../../src/store/indexeddb-local-backend"; @@ -28,7 +30,7 @@ function setupWorker(worker: IndexedDBStoreWorker): void { describe("IndexedDBStore Worker", () => { it("should pass 'closed' event via postMessage", async () => { const deferred = defer(); - const postMessage = jest.fn().mockImplementation(({ seq, command }) => { + const postMessage = vi.fn().mockImplementation(({ seq, command }) => { if (seq === 1 && command === "cmd_success") { deferred.resolve(); } diff --git a/spec/unit/stores/indexeddb.spec.ts b/spec/unit/stores/indexeddb.spec.ts index 74e4562bfdd..f388506c309 100644 --- a/spec/unit/stores/indexeddb.spec.ts +++ b/spec/unit/stores/indexeddb.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { afterEach, describe, expect, it, vi } from "vitest"; + import "fake-indexeddb/auto"; import "jest-localstorage-mock"; import { IDBFactory } from "fake-indexeddb"; @@ -25,7 +27,7 @@ import { defer } from "../../../src/utils"; describe("IndexedDBStore", () => { afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); const roomId = "!room:id"; @@ -134,8 +136,8 @@ describe("IndexedDBStore", () => { }); it("should use MemoryStore methods for pending events if no localStorage", async () => { - jest.spyOn(MemoryStore.prototype, "setPendingEvents"); - jest.spyOn(MemoryStore.prototype, "getPendingEvents"); + vi.spyOn(MemoryStore.prototype, "setPendingEvents"); + vi.spyOn(MemoryStore.prototype, "getPendingEvents"); const store = new IndexedDBStore({ indexedDB: indexedDB, @@ -151,8 +153,8 @@ describe("IndexedDBStore", () => { }); it("should persist pending events to localStorage if available", async () => { - jest.spyOn(MemoryStore.prototype, "setPendingEvents"); - jest.spyOn(MemoryStore.prototype, "getPendingEvents"); + vi.spyOn(MemoryStore.prototype, "setPendingEvents"); + vi.spyOn(MemoryStore.prototype, "getPendingEvents"); const store = new IndexedDBStore({ indexedDB: indexedDB, @@ -313,7 +315,7 @@ describe("IndexedDBStore", () => { }); it("remote worker should terminate upon destroy call", async () => { - const terminate = jest.fn(); + const terminate = vi.fn(); const worker = new (class MockWorker { private onmessage!: (data: any) => void; postMessage(data: any) { diff --git a/spec/unit/stores/memory.spec.ts b/spec/unit/stores/memory.spec.ts index 30d6cff45f9..59098e9e9cc 100644 --- a/spec/unit/stores/memory.spec.ts +++ b/spec/unit/stores/memory.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { describe, expect, it } from "vitest"; + import { MatrixEvent, MemoryStore } from "../../../src"; describe("MemoryStore", () => { diff --git a/spec/unit/sync-accumulator.spec.ts b/spec/unit/sync-accumulator.spec.ts index 8ece656e85a..1e615fd6bee 100644 --- a/spec/unit/sync-accumulator.spec.ts +++ b/spec/unit/sync-accumulator.spec.ts @@ -15,6 +15,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + import { ReceiptType } from "../../src/@types/read_receipts"; import { Category, @@ -795,7 +797,7 @@ describe("SyncAccumulator", function () { } afterEach(() => { - jest.spyOn(globalThis.Date, "now").mockRestore(); + vi.spyOn(globalThis.Date, "now").mockRestore(); }); it("should copy summary properties", function () { @@ -852,11 +854,11 @@ describe("SyncAccumulator", function () { const delta = 1000; const startingTs = 1000; - jest.spyOn(globalThis.Date, "now").mockReturnValue(startingTs); + vi.spyOn(globalThis.Date, "now").mockReturnValue(startingTs); sa.accumulate(RES_WITH_AGE); - jest.spyOn(globalThis.Date, "now").mockReturnValue(startingTs + delta); + vi.spyOn(globalThis.Date, "now").mockReturnValue(startingTs + delta); const output = sa.getJSON(); expect(output.roomsData.join["!foo:bar"].timeline.events[0].unsigned?.age).toEqual( diff --git a/spec/unit/testing.spec.ts b/spec/unit/testing.spec.ts index 32d24a47e0b..11c0929325e 100644 --- a/spec/unit/testing.spec.ts +++ b/spec/unit/testing.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { describe, expect, it } from "vitest"; + import { decryptExistingEvent, mkDecryptionFailureMatrixEvent, diff --git a/spec/unit/thread-utils.spec.ts b/spec/unit/thread-utils.spec.ts index 8bab97bd870..be38e036e2b 100644 --- a/spec/unit/thread-utils.spec.ts +++ b/spec/unit/thread-utils.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { describe, expect, it } from "vitest"; + import { IEvent } from "../../src"; import { secureRandomString } from "../../src/randomstring"; import { getRelationsThreadFilter } from "../../src/thread-utils"; diff --git a/spec/unit/timeline-window.spec.ts b/spec/unit/timeline-window.spec.ts index be52abe86be..6612205c4bc 100644 --- a/spec/unit/timeline-window.spec.ts +++ b/spec/unit/timeline-window.spec.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { MockedObject } from "jest-mock"; +import { beforeEach, describe, expect, it, MockedObject, vi } from "vitest"; import { MatrixClient } from "../../src/client"; import { EventTimelineSet } from "../../src/models/event-timeline-set"; @@ -27,10 +27,10 @@ import { MatrixEvent } from "../../src/models/event"; const ROOM_ID = "roomId"; const USER_ID = "userId"; const mockClient = { - getEventTimeline: jest.fn(), - paginateEventTimeline: jest.fn(), - supportsThreads: jest.fn(), - getUserId: jest.fn().mockReturnValue(USER_ID), + getEventTimeline: vi.fn(), + paginateEventTimeline: vi.fn(), + supportsThreads: vi.fn(), + getUserId: vi.fn().mockReturnValue(USER_ID), } as unknown as MockedObject; /* @@ -40,7 +40,7 @@ const mockClient = { function createTimeline(numEvents = 3, baseIndex = 1): EventTimeline { const room = new Room(ROOM_ID, mockClient, USER_ID); const timelineSet = new EventTimelineSet(room); - jest.spyOn(room, "getUnfilteredTimelineSet").mockReturnValue(timelineSet); + vi.spyOn(room, "getUnfilteredTimelineSet").mockReturnValue(timelineSet); const timeline = new EventTimeline(timelineSet); @@ -97,7 +97,7 @@ function createLinkedTimelines(): [EventTimeline, EventTimeline] { describe("TimelineIndex", function () { beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); mockClient.getEventTimeline.mockResolvedValue(undefined); }); @@ -192,7 +192,7 @@ describe("TimelineWindow", function () { } beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); mockClient.getEventTimeline.mockResolvedValue(undefined); mockClient.paginateEventTimeline.mockResolvedValue(false); }); @@ -202,7 +202,7 @@ describe("TimelineWindow", function () { const liveTimeline = createTimeline(); const room = new Room(ROOM_ID, mockClient, USER_ID); const timelineSet = new EventTimelineSet(room); - jest.spyOn(timelineSet, "getLiveTimeline").mockReturnValue(liveTimeline); + vi.spyOn(timelineSet, "getLiveTimeline").mockReturnValue(liveTimeline); const timelineWindow = new TimelineWindow(mockClient, timelineSet); await timelineWindow.load(undefined, 2); diff --git a/spec/unit/user.spec.ts b/spec/unit/user.spec.ts index 089fcc91e64..165f4f683bf 100644 --- a/spec/unit/user.spec.ts +++ b/spec/unit/user.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { beforeEach, describe, expect, it } from "vitest"; + import { User, UserEvent } from "../../src/models/user"; import { mkEvent } from "../test-utils/test-utils"; diff --git a/spec/unit/utils.spec.ts b/spec/unit/utils.spec.ts index d0c983f81f2..066561fbafb 100644 --- a/spec/unit/utils.spec.ts +++ b/spec/unit/utils.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { it, describe, expect } from "vitest"; + import * as utils from "../../src/utils"; import { alphabetPad, @@ -149,11 +151,9 @@ describe("utils", function () { describe("deepCompare", function () { const assert = { isTrue: function (x: any) { - // eslint-disable-next-line jest/no-standalone-expect expect(x).toBe(true); }, isFalse: function (x: any) { - // eslint-disable-next-line jest/no-standalone-expect expect(x).toBe(false); }, }; diff --git a/spec/unit/webrtc/call.spec.ts b/spec/unit/webrtc/call.spec.ts index 922d63bd738..163caa33104 100644 --- a/spec/unit/webrtc/call.spec.ts +++ b/spec/unit/webrtc/call.spec.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { mocked } from "jest-mock"; +import { afterEach, beforeEach, describe, expect, it, Mock, vi } from "vitest"; import { TestClient } from "../../TestClient"; import { @@ -48,6 +48,7 @@ import { import { CallFeed } from "../../../src/webrtc/callFeed"; import { EventType, IContent, ISendEventResponse, MatrixEvent, Room } from "../../../src"; import { emitPromise } from "../../test-utils/test-utils"; +import { mocked } from "../../test-utils"; const FAKE_ROOM_ID = "!foo:bar"; const CALL_LIFETIME = 60000; @@ -57,7 +58,7 @@ const startVoiceCall = async (client: TestClient, call: MatrixCall, userId?: str await client.httpBackend!.flush(""); await callPromise; - call.getOpponentMember = jest.fn().mockReturnValue({ userId: userId ?? "@bob:bar.uk" }); + call.getOpponentMember = vi.fn().mockReturnValue({ userId: userId ?? "@bob:bar.uk" }); }; const startVideoCall = async (client: TestClient, call: MatrixCall, userId?: string): Promise => { @@ -65,12 +66,12 @@ const startVideoCall = async (client: TestClient, call: MatrixCall, userId?: str await client.httpBackend.flush(""); await callPromise; - call.getOpponentMember = jest.fn().mockReturnValue({ userId: userId ?? "@bob:bar.uk" }); + call.getOpponentMember = vi.fn().mockReturnValue({ userId: userId ?? "@bob:bar.uk" }); }; const fakeIncomingCall = async (client: TestClient, call: MatrixCall, version: string | number = "1") => { const callPromise = call.initWithInvite({ - getContent: jest.fn().mockReturnValue({ + getContent: vi.fn().mockReturnValue({ version, call_id: "call_id", party_id: "remote_party_id", @@ -110,25 +111,18 @@ function makeMockEvent(sender: string, content: Record): MatrixEven describe("Call", function () { let client: TestClient; let call: MatrixCall; - let prevNavigator: Navigator; - let prevDocument: Document; - let prevWindow: Window & typeof globalThis; // We retain a reference to this in the correct Mock type - let mockSendEvent: jest.Mock, [string, string, IContent, string]>; + let mockSendEvent: Mock, [string, string, IContent, string]>; const errorListener = () => {}; beforeEach(function () { - prevNavigator = globalThis.navigator; - prevDocument = globalThis.document; - prevWindow = globalThis.window; - installWebRTCMocks(); client = new TestClient("@alice:foo", "somedevice", "token", undefined, {}); // We just stub out sendEvent: we're not interested in testing the client's // event sending code here - client.client.sendEvent = mockSendEvent = jest.fn(); + client.client.sendEvent = mockSendEvent = vi.fn(); { // in which we do naughty assignments to private members const untypedClient = client.client as any; @@ -144,7 +138,7 @@ describe("Call", function () { }, } as unknown as Room; }; - client.client.getProfileInfo = jest.fn(); + client.client.getProfileInfo = vi.fn(); call = new MatrixCall({ client: client.client, @@ -159,11 +153,8 @@ describe("Call", function () { call.hangup(CallErrorCode.UserHangup, true); client.stop(); - globalThis.navigator = prevNavigator; - globalThis.window = prevWindow; - globalThis.document = prevDocument; - jest.useRealTimers(); + vi.useRealTimers(); }); it("should ignore candidate events from non-matching party ID", async function () { @@ -180,7 +171,7 @@ describe("Call", function () { }), ); - const mockAddIceCandidate = (call.peerConn!.addIceCandidate = jest.fn()); + const mockAddIceCandidate = (call.peerConn!.addIceCandidate = vi.fn()); call.onRemoteIceCandidatesReceived( makeMockEvent("@test:foo", { version: 1, @@ -214,7 +205,7 @@ describe("Call", function () { it("should add candidates received before answer if party ID is correct", async function () { await startVoiceCall(client, call); - const mockAddIceCandidate = (call.peerConn!.addIceCandidate = jest.fn()); + const mockAddIceCandidate = (call.peerConn!.addIceCandidate = vi.fn()); call.onRemoteIceCandidatesReceived( makeMockEvent("@test:foo", { @@ -277,7 +268,7 @@ describe("Call", function () { }), ); - const identChangedCallback = jest.fn(); + const identChangedCallback = vi.fn(); call.on(CallEvent.AssertedIdentityChanged, identChangedCallback); await call.onAssertedIdentityReceived( @@ -346,7 +337,7 @@ describe("Call", function () { }), ); - const mockScreenshareNoMetadata = ((call as any).setScreensharingEnabledWithoutMetadataSupport = jest.fn()); + const mockScreenshareNoMetadata = ((call as any).setScreensharingEnabledWithoutMetadataSupport = vi.fn()); call.setScreensharingEnabled(true); expect(mockScreenshareNoMetadata).toHaveBeenCalled(); @@ -356,7 +347,7 @@ describe("Call", function () { await client.httpBackend!.flush(""); (call as any).shouldAnswerWithMediaType = (wantedValue: boolean) => wantedValue; - client.client.getMediaHandler().getUserMediaStream = jest.fn().mockRejectedValue("reject"); + client.client.getMediaHandler().getUserMediaStream = vi.fn().mockRejectedValue("reject"); await call.answer(true, true); @@ -365,7 +356,7 @@ describe("Call", function () { }); it("should handle mid-call device changes", async () => { - client.client.getMediaHandler().getUserMediaStream = jest + client.client.getMediaHandler().getUserMediaStream = vi .fn() .mockReturnValue( new MockMediaStream("stream", [ @@ -434,7 +425,7 @@ describe("Call", function () { }); it("should handle error on call upgrade", async () => { - const onError = jest.fn(); + const onError = vi.fn(); call.on(CallEvent.Error, onError); await startVoiceCall(client, call); @@ -451,7 +442,7 @@ describe("Call", function () { }), ); - const mockGetUserMediaStream = jest.fn().mockRejectedValue(new Error("Test error")); + const mockGetUserMediaStream = vi.fn().mockRejectedValue(new Error("Test error")); client.client.getMediaHandler().getUserMediaStream = mockGetUserMediaStream; // then unmute which should cause an upgrade @@ -559,7 +550,7 @@ describe("Call", function () { beforeEach(async () => { // start an incoming call, but add no feeds await call.initWithInvite({ - getContent: jest.fn().mockReturnValue({ + getContent: vi.fn().mockReturnValue({ version: "1", call_id: "call_id", party_id: "remote_party_id", @@ -574,14 +565,14 @@ describe("Call", function () { }); it("if no video", async () => { - call.getOpponentMember = jest.fn().mockReturnValue({ userId: "@bob:bar.uk" }); + call.getOpponentMember = vi.fn().mockReturnValue({ userId: "@bob:bar.uk" }); (call as any).pushRemoteFeed(new MockMediaStream("remote_stream1", [])); expect(call.type).toBe(CallType.Voice); }); it("if remote video", async () => { - call.getOpponentMember = jest.fn().mockReturnValue({ userId: "@bob:bar.uk" }); + call.getOpponentMember = vi.fn().mockReturnValue({ userId: "@bob:bar.uk" }); (call as any).pushRemoteFeed( new MockMediaStream("remote_stream1", [new MockMediaStreamTrack("track_id", "video")]), @@ -590,7 +581,7 @@ describe("Call", function () { }); it("if local video", async () => { - call.getOpponentMember = jest.fn().mockReturnValue({ userId: "@bob:bar.uk" }); + call.getOpponentMember = vi.fn().mockReturnValue({ userId: "@bob:bar.uk" }); // since this is testing for the presence of a local sender, we need to add a transciever // rather than just a source track @@ -625,7 +616,7 @@ describe("Call", function () { ]); await client.httpBackend!.flush(""); await callPromise; - call.getOpponentMember = jest.fn().mockReturnValue({ userId: "@bob:bar.uk" }); + call.getOpponentMember = vi.fn().mockReturnValue({ userId: "@bob:bar.uk" }); (call as any).pushNewLocalFeed( new MockMediaStream("local_stream2", [ @@ -677,7 +668,7 @@ describe("Call", function () { ]); await client.httpBackend!.flush(""); await callPromise; - call.getOpponentMember = jest.fn().mockReturnValue({ userId: "@bob:bar.uk" }); + call.getOpponentMember = vi.fn().mockReturnValue({ userId: "@bob:bar.uk" }); (call as any).updateRemoteSDPStreamMetadata({ remote_usermedia_stream_id: { @@ -709,7 +700,7 @@ describe("Call", function () { it("should end call after receiving a select event with a different party id", async () => { await fakeIncomingCall(client, call); - const callHangupCallback = jest.fn(); + const callHangupCallback = vi.fn(); call.on(CallEvent.Hangup, callHangupCallback); await call.onSelectAnswerReceived( @@ -761,7 +752,7 @@ describe("Call", function () { it("should handle creating a data channel", async () => { await startVoiceCall(client, call); - const dataChannelCallback = jest.fn(); + const dataChannelCallback = vi.fn(); call.on(CallEvent.DataChannel, dataChannelCallback); const dataChannel = call.createDataChannel("data_channel_label", { id: 123 }); @@ -774,7 +765,7 @@ describe("Call", function () { it("should emit a data channel event when the other side adds a data channel", async () => { await startVoiceCall(client, call); - const dataChannelCallback = jest.fn(); + const dataChannelCallback = vi.fn(); call.on(CallEvent.DataChannel, dataChannelCallback); (call.peerConn as unknown as MockRTCPeerConnection).triggerIncomingDataChannel(); @@ -789,9 +780,10 @@ describe("Call", function () { it("should return false if window or document are undefined", () => { globalThis.window = undefined!; + vi.stubGlobal("window", undefined); expect(supportsMatrixCall()).toBe(false); - globalThis.window = prevWindow; - globalThis.document = undefined!; + vi.unstubAllGlobals(); + vi.stubGlobal("document", undefined); expect(supportsMatrixCall()).toBe(false); }); @@ -822,14 +814,14 @@ describe("Call", function () { describe("ignoring streams with ids for which we already have a feed", () => { const STREAM_ID = "stream_id"; - let FEEDS_CHANGED_CALLBACK: jest.Mock; + let FEEDS_CHANGED_CALLBACK: Mock; beforeEach(async () => { - FEEDS_CHANGED_CALLBACK = jest.fn(); + FEEDS_CHANGED_CALLBACK = vi.fn(); await startVoiceCall(client, call); call.on(CallEvent.FeedsChanged, FEEDS_CHANGED_CALLBACK); - jest.spyOn(call, "pushLocalFeed"); + vi.spyOn(call, "pushLocalFeed"); }); afterEach(() => { @@ -882,7 +874,7 @@ describe("Call", function () { describe("transferToCall", () => { it("should send the required events", async () => { const targetCall = new MatrixCall({ client: client.client, roomId: "!roomId:server" }); - const sendEvent = jest.spyOn(client.client, "sendEvent"); + const sendEvent = vi.spyOn(client.client, "sendEvent"); await call.transferToCall(targetCall); const newCallId = (sendEvent.mock.calls[0][2] as any)!.await_call; @@ -897,14 +889,14 @@ describe("Call", function () { }); describe("muting", () => { - let mockSendVoipEvent: jest.Mock, [string, object]>; + let mockSendVoipEvent: Mock, [string, object]>; beforeEach(async () => { - (call as any).sendVoipEvent = mockSendVoipEvent = jest.fn(); + (call as any).sendVoipEvent = mockSendVoipEvent = vi.fn(); await startVideoCall(client, call); }); afterEach(() => { - jest.useRealTimers(); + vi.useRealTimers(); }); it("should not remove video sender on video mute", async () => { @@ -913,19 +905,19 @@ describe("Call", function () { }); it("should release camera after short delay on video mute", async () => { - jest.useFakeTimers(); + vi.useFakeTimers(); await call.setLocalVideoMuted(true); - jest.advanceTimersByTime(500); + vi.advanceTimersByTime(500); expect(call.hasLocalUserMediaVideoTrack).toBe(false); }); it("should re-request video feed on video unmute if it doesn't have one", async () => { - jest.useFakeTimers(); + vi.useFakeTimers(); - const mockGetUserMediaStream = jest + const mockGetUserMediaStream = vi .fn() .mockReturnValue(client.client.getMediaHandler().getUserMediaStream(true, true)); @@ -933,7 +925,7 @@ describe("Call", function () { await call.setLocalVideoMuted(true); - jest.advanceTimersByTime(500); + vi.advanceTimersByTime(500); await call.setLocalVideoMuted(false); @@ -941,7 +933,7 @@ describe("Call", function () { }); it("should not release camera on fast mute and unmute", async () => { - const mockGetUserMediaStream = jest.fn(); + const mockGetUserMediaStream = vi.fn(); client.client.getMediaHandler().getUserMediaStream = mockGetUserMediaStream; @@ -1098,7 +1090,7 @@ describe("Call", function () { } // We might not always be in fake timer mode, but it's // fine to run this if not, so we just call it anyway. - jest.runOnlyPendingTimers(); + vi.runOnlyPendingTimers(); try { expect(mockSendEvent).toHaveBeenCalledWith(...args); return; @@ -1132,7 +1124,7 @@ describe("Call", function () { candidate: fakeCandidateString, sdpMLineIndex: 0, sdpMid: "0", - toJSON: jest.fn().mockReturnValue(fakeCandidateString), + toJSON: vi.fn().mockReturnValue(fakeCandidateString), }, } as unknown as RTCPeerConnectionIceEvent; @@ -1143,7 +1135,7 @@ describe("Call", function () { }); afterEach(() => { - jest.useRealTimers(); + vi.useRealTimers(); }); it("sends ICE candidates as separate events if they arrive after the answer", async () => { @@ -1159,7 +1151,7 @@ describe("Call", function () { }); it("retries sending ICE candidates", async () => { - jest.useFakeTimers(); + vi.useFakeTimers(); mockSendEvent.mockRejectedValueOnce(new Error("Fake error")); @@ -1185,7 +1177,7 @@ describe("Call", function () { }); it("gives up on call after 5 attempts at sending ICE candidates", async () => { - jest.useFakeTimers(); + vi.useFakeTimers(); mockSendEvent.mockImplementation((roomId: string, eventType: string) => { if (eventType === EventType.CallCandidates) { @@ -1198,7 +1190,7 @@ describe("Call", function () { mockPeerConn!.iceCandidateListener!(fakeCandidateEvent); while (!call.callHasEnded()) { - jest.runOnlyPendingTimers(); + vi.runOnlyPendingTimers(); await untilEventSent( FAKE_ROOM_ID, EventType.CallCandidates, @@ -1217,12 +1209,12 @@ describe("Call", function () { }); it("times out an incoming call", async () => { - jest.useFakeTimers(); + vi.useFakeTimers(); await fakeIncomingCall(client, call, "1"); expect(call.state).toEqual(CallState.Ringing); - jest.advanceTimersByTime(CALL_LIFETIME + 1000); + vi.advanceTimersByTime(CALL_LIFETIME + 1000); expect(call.state).toEqual(CallState.Ended); }); @@ -1379,7 +1371,7 @@ describe("Call", function () { MockRTCPeerConnection.triggerAllNegotiations(); const mockVideoSender = call.peerConn!.getSenders().find((s) => s.track!.kind === "video"); - const mockReplaceTrack = (mockVideoSender!.replaceTrack = jest.fn()); + const mockReplaceTrack = (mockVideoSender!.replaceTrack = vi.fn()); await call.setScreensharingEnabled(true); @@ -1443,8 +1435,8 @@ describe("Call", function () { roomId: FAKE_ROOM_ID, }); - const callHangupListener = jest.fn(); - const newCallHangupListener = jest.fn(); + const callHangupListener = vi.fn(); + const newCallHangupListener = vi.fn(); call.on(CallEvent.Hangup, callHangupListener); newCall.on(CallEvent.Error, () => {}); @@ -1484,7 +1476,7 @@ describe("Call", function () { it("transfers a call to another user", async () => { // @ts-ignore Mock - jest.spyOn(call, "terminate"); + vi.spyOn(call, "terminate"); await startVoiceCall(client, call, ALICE_USER_ID); await call.transfer(BOB_USER_ID); @@ -1508,7 +1500,7 @@ describe("Call", function () { describe("onTrack", () => { it("ignores streamless track", async () => { // @ts-ignore Mock pushRemoteFeed() is private - jest.spyOn(call, "pushRemoteFeed"); + vi.spyOn(call, "pushRemoteFeed"); await call.placeVoiceCall(); @@ -1523,7 +1515,7 @@ describe("Call", function () { it("correctly pushes", async () => { // @ts-ignore Mock pushRemoteFeed() is private - jest.spyOn(call, "pushRemoteFeed"); + vi.spyOn(call, "pushRemoteFeed"); await call.placeVoiceCall(); await call.onAnswerReceived( @@ -1564,7 +1556,7 @@ describe("Call", function () { expect(call.callHasEnded()).toBe(false); await call.initWithInvite({ - getContent: jest.fn().mockReturnValue({ + getContent: vi.fn().mockReturnValue({ version: "1", call_id: "call_id", party_id: "remote_party_id", @@ -1624,16 +1616,16 @@ describe("Call", function () { it("should correctly emit LengthChanged", async () => { const advanceByArray = [2, 3, 5]; - const lengthChangedListener = jest.fn(); + const lengthChangedListener = vi.fn(); - jest.useFakeTimers(); + vi.useFakeTimers(); call.addListener(CallEvent.LengthChanged, lengthChangedListener); await fakeIncomingCall(client, call, "1"); (call.peerConn as unknown as MockRTCPeerConnection).iceConnectionStateChangeListener!(); let hasAdvancedBy = 0; for (const advanceBy of advanceByArray) { - jest.advanceTimersByTime(advanceBy * 1000); + vi.advanceTimersByTime(advanceBy * 1000); hasAdvancedBy += advanceBy; expect(lengthChangedListener).toHaveBeenCalledTimes(hasAdvancedBy); @@ -1645,24 +1637,24 @@ describe("Call", function () { let mockPeerConn: MockRTCPeerConnection; beforeEach(async () => { - jest.useFakeTimers(); - jest.spyOn(call, "hangup"); + vi.useFakeTimers(); + vi.spyOn(call, "hangup"); await fakeIncomingCall(client, call, "1"); mockPeerConn = call.peerConn as unknown as MockRTCPeerConnection; mockPeerConn.iceConnectionState = "disconnected"; mockPeerConn.iceConnectionStateChangeListener!(); - jest.spyOn(mockPeerConn, "restartIce"); + vi.spyOn(mockPeerConn, "restartIce"); }); it("should restart ICE gathering after being disconnected for 2 seconds", () => { - jest.advanceTimersByTime(3 * 1000); + vi.advanceTimersByTime(3 * 1000); expect(mockPeerConn.restartIce).toHaveBeenCalled(); }); it("should hang up after being disconnected for 30 seconds", () => { - jest.advanceTimersByTime(31 * 1000); + vi.advanceTimersByTime(31 * 1000); expect(call.hangup).toHaveBeenCalledWith(CallErrorCode.IceFailed, false); }); @@ -1683,14 +1675,14 @@ describe("Call", function () { it("should not hangup if we've managed to re-connect", () => { mockPeerConn.iceConnectionState = "connected"; mockPeerConn.iceConnectionStateChangeListener!(); - jest.advanceTimersByTime(31 * 1000); + vi.advanceTimersByTime(31 * 1000); expect(call.hangup).not.toHaveBeenCalled(); }); }); describe("Call replace", () => { it("Fires event when call replaced", async () => { - const onReplace = jest.fn(); + const onReplace = vi.fn(); call.on(CallEvent.Replaced, onReplace); await call.placeVoiceCall(); @@ -1713,13 +1705,13 @@ describe("Call", function () { call.hangup = () => null; call.isLocalOnHold = () => true; // @ts-ignore - call.updateRemoteSDPStreamMetadata = jest.fn(); + call.updateRemoteSDPStreamMetadata = vi.fn(); // @ts-ignore - call.getRidOfRTXCodecs = jest.fn(); + call.getRidOfRTXCodecs = vi.fn(); // @ts-ignore - call.createAnswer = jest.fn().mockResolvedValue({}); + call.createAnswer = vi.fn().mockResolvedValue({}); // @ts-ignore - call.sendVoipEvent = jest.fn(); + call.sendVoipEvent = vi.fn(); }); it("and reject remote offer if not polite and have pending local offer", async () => { @@ -1737,7 +1729,7 @@ describe("Call", function () { // @ts-ignore call.peerConn = { signalingState: "have-local-offer", - setRemoteDescription: jest.fn(), + setRemoteDescription: vi.fn(), }; await call.onNegotiateReceived(offerEvent); expect(call.peerConn?.setRemoteDescription).not.toHaveBeenCalled(); @@ -1765,7 +1757,7 @@ describe("Call", function () { // @ts-ignore call.peerConn = { signalingState: "have-local-offer", - setRemoteDescription: jest.fn(), + setRemoteDescription: vi.fn(), }; await call.onNegotiateReceived(offerEvent); expect(call.peerConn?.setRemoteDescription).toHaveBeenCalled(); @@ -1786,7 +1778,7 @@ describe("Call", function () { // @ts-ignore call.peerConn = { signalingState: "stable", - setRemoteDescription: jest.fn(), + setRemoteDescription: vi.fn(), }; await call.onNegotiateReceived(offerEvent); expect(call.peerConn?.setRemoteDescription).toHaveBeenCalled(); @@ -1807,7 +1799,7 @@ describe("Call", function () { // @ts-ignore call.peerConn = { signalingState: "have-local-offer", - setRemoteDescription: jest.fn(), + setRemoteDescription: vi.fn(), }; await call.onNegotiateReceived(offerEvent); expect(call.peerConn?.setRemoteDescription).toHaveBeenCalled(); diff --git a/spec/unit/webrtc/callEventHandler.spec.ts b/spec/unit/webrtc/callEventHandler.spec.ts index 82826363b83..cf64b99b1aa 100644 --- a/spec/unit/webrtc/callEventHandler.spec.ts +++ b/spec/unit/webrtc/callEventHandler.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + import { TestClient } from "../../TestClient"; import { ClientEvent, @@ -46,7 +48,7 @@ describe("CallEventHandler", () => { client.callEventHandler.start(); client.groupCallEventHandler = new GroupCallEventHandler(client); client.groupCallEventHandler.start(); - client.sendStateEvent = jest.fn().mockResolvedValue({}); + client.sendStateEvent = vi.fn().mockResolvedValue({}); }); afterEach(() => { @@ -55,7 +57,7 @@ describe("CallEventHandler", () => { }); const sync = async () => { - client.getSyncState = jest.fn().mockReturnValue(SyncState.Syncing); + client.getSyncState = vi.fn().mockReturnValue(SyncState.Syncing); client.emit(ClientEvent.Sync, SyncState.Syncing, SyncState.Prepared); // We can't await the event processing @@ -149,10 +151,10 @@ describe("CallEventHandler", () => { }); client.emit(RoomEvent.Timeline, callHangup, room, false, false, timelineData); - const incomingCallEmitted = jest.fn(); + const incomingCallEmitted = vi.fn(); client.on(CallEventHandlerEvent.Incoming, incomingCallEmitted); - client.getSyncState = jest.fn().mockReturnValue(SyncState.Syncing); + client.getSyncState = vi.fn().mockReturnValue(SyncState.Syncing); client.emit(ClientEvent.Sync, SyncState.Syncing, null); expect(incomingCallEmitted).not.toHaveBeenCalled(); @@ -160,8 +162,8 @@ describe("CallEventHandler", () => { it("should ignore non-call events", async () => { // @ts-ignore Mock handleCallEvent is private - jest.spyOn(client.callEventHandler, "handleCallEvent"); - jest.spyOn(client, "checkTurnServers").mockReturnValue(Promise.resolve(true)); + vi.spyOn(client.callEventHandler, "handleCallEvent"); + vi.spyOn(client, "checkTurnServers").mockReturnValue(Promise.resolve(true)); const room = new Room("!room:id", client, "@user:id"); const timelineData: IRoomTimelineData = { timeline: new EventTimeline(new EventTimelineSet(room, {})) }; @@ -187,7 +189,7 @@ describe("CallEventHandler", () => { }); describe("handleCallEvent()", () => { - const incomingCallListener = jest.fn(); + const incomingCallListener = vi.fn(); let timelineData: IRoomTimelineData; let room: Room; @@ -195,16 +197,16 @@ describe("CallEventHandler", () => { room = new Room("!room:id", client, client.getUserId()!); timelineData = { timeline: new EventTimeline(new EventTimelineSet(room, {})) }; - jest.spyOn(client, "checkTurnServers").mockReturnValue(Promise.resolve(true)); - jest.spyOn(client, "getRoom").mockReturnValue(room); - jest.spyOn(room, "getMember").mockReturnValue({ user_id: client.getUserId() } as unknown as RoomMember); + vi.spyOn(client, "checkTurnServers").mockReturnValue(Promise.resolve(true)); + vi.spyOn(client, "getRoom").mockReturnValue(room); + vi.spyOn(room, "getMember").mockReturnValue({ user_id: client.getUserId() } as unknown as RoomMember); client.on(CallEventHandlerEvent.Incoming, incomingCallListener); }); afterEach(() => { MockRTCPeerConnection.resetInstances(); - jest.resetAllMocks(); + vi.resetAllMocks(); }); it("should create a call when receiving an invite", async () => { @@ -240,9 +242,9 @@ describe("CallEventHandler", () => { const DEVICE_ID = "device_id"; incomingCallListener.mockImplementation((c) => (call = c)); - jest.spyOn(client.groupCallEventHandler!, "getGroupCallById").mockReturnValue(groupCall); + vi.spyOn(client.groupCallEventHandler!, "getGroupCallById").mockReturnValue(groupCall); // @ts-ignore Mock onIncomingCall is private - jest.spyOn(groupCall, "onIncomingCall"); + vi.spyOn(groupCall, "onIncomingCall"); await groupCall.enter(); client.emit( diff --git a/spec/unit/webrtc/callFeed.spec.ts b/spec/unit/webrtc/callFeed.spec.ts index bce56261e0f..c9176465af3 100644 --- a/spec/unit/webrtc/callFeed.spec.ts +++ b/spec/unit/webrtc/callFeed.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + import { SDPStreamMetadataPurpose } from "../../../src/webrtc/callEventTypes"; import { CallFeed } from "../../../src/webrtc/callFeed"; import { TestClient } from "../../TestClient"; @@ -93,7 +95,7 @@ describe("CallFeed", () => { it.each([true, false])("should always be connected, if isLocal()", (val: boolean) => { // @ts-ignore feed._connected = val; - jest.spyOn(feed, "isLocal").mockReturnValue(true); + vi.spyOn(feed, "isLocal").mockReturnValue(true); expect(feed.connected).toBeTruthy(); }); diff --git a/spec/unit/webrtc/groupCall.spec.ts b/spec/unit/webrtc/groupCall.spec.ts index 669542e7613..08008b75991 100644 --- a/spec/unit/webrtc/groupCall.spec.ts +++ b/spec/unit/webrtc/groupCall.spec.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { mocked } from "jest-mock"; +import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, Mock, MockedFunction, vi } from "vitest"; import { EventType, GroupCallIntent, GroupCallType, MatrixCall, MatrixEvent, Room, RoomMember } from "../../../src"; import { RoomStateEvent } from "../../../src/models/room-state"; @@ -129,7 +129,7 @@ describe("Group Call", function () { }); describe("Basic functionality", function () { - let mockSendState: jest.Mock; + let mockSendState: MockedFunction; let mockClient: MatrixClient; let room: Room; let groupCall: GroupCall; @@ -162,7 +162,7 @@ describe("Group Call", function () { ); it.each([0, 3, 5, 10, 5000])("sets correct creation timestamp when creating a call", async (time: number) => { - jest.spyOn(Date, "now").mockReturnValue(time); + vi.spyOn(Date, "now").mockReturnValue(time); await groupCall.create(); expect(groupCall.creationTs).toBe(time); @@ -170,7 +170,7 @@ describe("Group Call", function () { it("does not initialize local call feed, if it already is", async () => { await groupCall.initLocalCallFeed(); - jest.spyOn(groupCall, "initLocalCallFeed"); + vi.spyOn(groupCall, "initLocalCallFeed"); await groupCall.enter(); expect(groupCall.initLocalCallFeed).not.toHaveBeenCalled(); @@ -188,7 +188,7 @@ describe("Group Call", function () { }); it("sets state to local call feed uninitialized when getUserMedia() fails", async () => { - jest.spyOn(mockClient.getMediaHandler(), "getUserMediaStream").mockRejectedValue("Error"); + vi.spyOn(mockClient.getMediaHandler(), "getUserMediaStream").mockRejectedValue("Error"); await expect(groupCall.initLocalCallFeed()).rejects.toBeTruthy(); expect(groupCall.state).toBe(GroupCallState.LocalCallFeedUninitialized); @@ -381,10 +381,10 @@ describe("Group Call", function () { const newFeed = new MockCallFeed(FAKE_USER_ID_1, FAKE_DEVICE_ID_1, new MockMediaStream("new")); beforeEach(async () => { - jest.spyOn(currentFeed, "dispose"); - jest.spyOn(newFeed, "measureVolumeActivity"); + vi.spyOn(currentFeed, "dispose"); + vi.spyOn(newFeed, "measureVolumeActivity"); - jest.spyOn(groupCall, "emit"); + vi.spyOn(groupCall, "emit"); call = new MockMatrixCall(room.roomId, groupCall.groupCallId); @@ -397,7 +397,7 @@ describe("Group Call", function () { it("ignores changes, if we can't get user id of opponent", async () => { const call = new MockMatrixCall(room.roomId, groupCall.groupCallId); - jest.spyOn(call, "getOpponentMember").mockReturnValue({ userId: undefined }); + vi.spyOn(call, "getOpponentMember").mockReturnValue({ userId: undefined }); // @ts-ignore Mock expect(() => groupCall.onCallFeedsChanged(call)).toThrow(); @@ -503,7 +503,7 @@ describe("Group Call", function () { }); afterEach(() => { - jest.useRealTimers(); + vi.useRealTimers(); groupCall.leave(); }); @@ -513,30 +513,30 @@ describe("Group Call", function () { }); it("re-mutes microphone after transmit timeout in PTT mode", async () => { - jest.useFakeTimers(); + vi.useFakeTimers(); await groupCall.setMicrophoneMuted(false); expect(groupCall.isMicrophoneMuted()).toEqual(false); - await jest.advanceTimersByTimeAsync(groupCall.pttMaxTransmitTime + 100); + await vi.advanceTimersByTimeAsync(groupCall.pttMaxTransmitTime + 100); expect(groupCall.isMicrophoneMuted()).toEqual(true); }); it("timer is cleared when mic muted again in PTT mode", async () => { - jest.useFakeTimers(); + vi.useFakeTimers(); await groupCall.setMicrophoneMuted(false); expect(groupCall.isMicrophoneMuted()).toEqual(false); // 'talk' for half the allowed time - jest.advanceTimersByTime(groupCall.pttMaxTransmitTime / 2); + vi.advanceTimersByTime(groupCall.pttMaxTransmitTime / 2); await groupCall.setMicrophoneMuted(true); await groupCall.setMicrophoneMuted(false); // we should still be unmuted after almost the full timeout duration // if not, the timer for the original talking session must have fired - jest.advanceTimersByTime(groupCall.pttMaxTransmitTime - 100); + vi.advanceTimersByTime(groupCall.pttMaxTransmitTime - 100); expect(groupCall.isMicrophoneMuted()).toEqual(false); }); @@ -553,7 +553,7 @@ describe("Group Call", function () { const metadataUpdatePromise = new Promise((resolve) => { metadataUpdateResolve = resolve; }); - mockCall.sendMetadataUpdate = jest.fn().mockReturnValue(metadataUpdatePromise); + mockCall.sendMetadataUpdate = vi.fn().mockReturnValue(metadataUpdatePromise); const mutePromise = groupCall.setMicrophoneMuted(false); // we should still be muted at this point because the metadata update hasn't sent @@ -583,13 +583,13 @@ describe("Group Call", function () { const metadataUpdatePromise = new Promise((resolve) => { metadataUpdateResolve = resolve; }); - mockCall.sendMetadataUpdate = jest.fn().mockReturnValue(metadataUpdatePromise); + mockCall.sendMetadataUpdate = vi.fn().mockReturnValue(metadataUpdatePromise); const getUserMediaStreamFlush = Promise.resolve("stream"); // @ts-ignore mockCall.cleint = { getMediaHandler: { - getUserMediaStream: jest.fn().mockReturnValue(getUserMediaStreamFlush), + getUserMediaStream: vi.fn().mockReturnValue(getUserMediaStreamFlush), }, }; const mutePromise = groupCall.setMicrophoneMuted(true); @@ -681,7 +681,7 @@ describe("Group Call", function () { afterEach(function () { groupCall1.leave(); groupCall2.leave(); - jest.useRealTimers(); + vi.useRealTimers(); MockRTCPeerConnection.resetInstances(); }); @@ -721,7 +721,7 @@ describe("Group Call", function () { }); it("Retries calls", async function () { - jest.useFakeTimers(); + vi.useFakeTimers(); await groupCall1.create(); try { @@ -753,7 +753,7 @@ describe("Group Call", function () { }); }); - jest.advanceTimersByTime(groupCall1.retryCallInterval + 500); + vi.advanceTimersByTime(groupCall1.retryCallInterval + 500); // when we placed the call, we could await on enter which waited for the call to // be made. We don't have that luxury now, so first have to wait for the call @@ -805,10 +805,10 @@ describe("Group Call", function () { // @ts-ignore const call = groupCall1.calls.get(client2.userId)!.get(client2.deviceId)!; - call.isMicrophoneMuted = jest.fn().mockReturnValue(true); - call.setMicrophoneMuted = jest.fn(); - call.isLocalVideoMuted = jest.fn().mockReturnValue(true); - call.setLocalVideoMuted = jest.fn(); + call.isMicrophoneMuted = vi.fn().mockReturnValue(true); + call.setMicrophoneMuted = vi.fn(); + call.isLocalVideoMuted = vi.fn().mockReturnValue(true); + call.setLocalVideoMuted = vi.fn(); call.emit(CallEvent.State, CallState.Connected, CallState.InviteSent, call); @@ -829,7 +829,7 @@ describe("Group Call", function () { mockClient = typedMockClient as unknown as MatrixClient; room = new Room(FAKE_ROOM_ID, mockClient, FAKE_USER_ID_1); - room.currentState.getStateEvents = jest.fn().mockImplementation(mockGetStateEvents()); + room.currentState.getStateEvents = vi.fn().mockImplementation(mockGetStateEvents()); room.currentState.members[FAKE_USER_ID_1] = { userId: FAKE_USER_ID_1, membership: KnownMembership.Join, @@ -844,14 +844,14 @@ describe("Group Call", function () { it("should mute local audio when calling setMicrophoneMuted()", async () => { const groupCall = await createAndEnterGroupCall(mockClient, room); - groupCall.localCallFeed!.setAudioVideoMuted = jest.fn(); + groupCall.localCallFeed!.setAudioVideoMuted = vi.fn(); const setAVMutedArray: ((audioMuted: boolean | null, videoMuted: boolean | null) => void)[] = []; const tracksArray: MediaStreamTrack[] = []; const sendMetadataUpdateArray: (() => Promise)[] = []; groupCall.forEachCall((call) => { - setAVMutedArray.push((call.localUsermediaFeed!.setAudioVideoMuted = jest.fn())); + setAVMutedArray.push((call.localUsermediaFeed!.setAudioVideoMuted = vi.fn())); tracksArray.push(...call.localUsermediaStream!.getAudioTracks()); - sendMetadataUpdateArray.push((call.sendMetadataUpdate = jest.fn())); + sendMetadataUpdateArray.push((call.sendMetadataUpdate = vi.fn())); }); await groupCall.setMicrophoneMuted(true); @@ -868,18 +868,18 @@ describe("Group Call", function () { it("should mute local video when calling setLocalVideoMuted()", async () => { const groupCall = await createAndEnterGroupCall(mockClient, room); - jest.spyOn(mockClient.getMediaHandler(), "getUserMediaStream"); - jest.spyOn(groupCall, "updateLocalUsermediaStream"); - jest.spyOn(groupCall.localCallFeed!, "setAudioVideoMuted"); + vi.spyOn(mockClient.getMediaHandler(), "getUserMediaStream"); + vi.spyOn(groupCall, "updateLocalUsermediaStream"); + vi.spyOn(groupCall.localCallFeed!, "setAudioVideoMuted"); const setAVMutedArray: ((audioMuted: boolean | null, videoMuted: boolean | null) => void)[] = []; const tracksArray: MediaStreamTrack[] = []; const sendMetadataUpdateArray: (() => Promise)[] = []; groupCall.forEachCall((call) => { - call.localUsermediaFeed!.isVideoMuted = jest.fn().mockReturnValue(true); - setAVMutedArray.push((call.localUsermediaFeed!.setAudioVideoMuted = jest.fn())); + call.localUsermediaFeed!.isVideoMuted = vi.fn().mockReturnValue(true); + setAVMutedArray.push((call.localUsermediaFeed!.setAudioVideoMuted = vi.fn())); tracksArray.push(...call.localUsermediaStream!.getVideoTracks()); - sendMetadataUpdateArray.push((call.sendMetadataUpdate = jest.fn())); + sendMetadataUpdateArray.push((call.sendMetadataUpdate = vi.fn())); }); await groupCall.setLocalVideoMuted(true); @@ -896,15 +896,15 @@ describe("Group Call", function () { it("returns false when unmuting audio with no audio device", async () => { const groupCall = await createAndEnterGroupCall(mockClient, room); - jest.spyOn(mockClient.getMediaHandler(), "hasAudioDevice").mockResolvedValue(false); + vi.spyOn(mockClient.getMediaHandler(), "hasAudioDevice").mockResolvedValue(false); expect(await groupCall.setMicrophoneMuted(false)).toBe(false); }); it("returns false when no permission for audio stream and localCallFeed do not have an audio track", async () => { const groupCall = await createAndEnterGroupCall(mockClient, room); // @ts-ignore - jest.spyOn(groupCall.localCallFeed, "hasAudioTrack", "get").mockReturnValue(false); - jest.spyOn(mockClient.getMediaHandler(), "getUserMediaStream").mockRejectedValueOnce( + vi.spyOn(groupCall.localCallFeed, "hasAudioTrack", "get").mockReturnValue(false); + vi.spyOn(mockClient.getMediaHandler(), "getUserMediaStream").mockRejectedValueOnce( new Error("No Permission"), ); expect(await groupCall.setMicrophoneMuted(false)).toBe(false); @@ -913,30 +913,30 @@ describe("Group Call", function () { it("returns false when user media stream null", async () => { const groupCall = await createAndEnterGroupCall(mockClient, room); // @ts-ignore - jest.spyOn(groupCall.localCallFeed, "hasAudioTrack", "get").mockReturnValue(false); + vi.spyOn(groupCall.localCallFeed, "hasAudioTrack", "get").mockReturnValue(false); // @ts-ignore - jest.spyOn(mockClient.getMediaHandler(), "getUserMediaStream").mockResolvedValue({} as MediaStream); + vi.spyOn(mockClient.getMediaHandler(), "getUserMediaStream").mockResolvedValue({} as MediaStream); expect(await groupCall.setMicrophoneMuted(false)).toBe(false); }); it("returns true when no permission for audio stream but localCallFeed has a audio track already", async () => { const groupCall = await createAndEnterGroupCall(mockClient, room); // @ts-ignore - jest.spyOn(groupCall.localCallFeed, "hasAudioTrack", "get").mockReturnValue(true); - jest.spyOn(mockClient.getMediaHandler(), "getUserMediaStream"); + vi.spyOn(groupCall.localCallFeed, "hasAudioTrack", "get").mockReturnValue(true); + vi.spyOn(mockClient.getMediaHandler(), "getUserMediaStream"); expect(mockClient.getMediaHandler().getUserMediaStream).not.toHaveBeenCalled(); expect(await groupCall.setMicrophoneMuted(false)).toBe(true); }); it("returns false when unmuting video with no video device", async () => { const groupCall = await createAndEnterGroupCall(mockClient, room); - jest.spyOn(mockClient.getMediaHandler(), "hasVideoDevice").mockResolvedValue(false); + vi.spyOn(mockClient.getMediaHandler(), "hasVideoDevice").mockResolvedValue(false); expect(await groupCall.setLocalVideoMuted(false)).toBe(false); }); it("returns false when no permission for video stream", async () => { const groupCall = await createAndEnterGroupCall(mockClient, room); - jest.spyOn(mockClient.getMediaHandler(), "getUserMediaStream").mockRejectedValueOnce( + vi.spyOn(mockClient.getMediaHandler(), "getUserMediaStream").mockRejectedValueOnce( new Error("No Permission"), ); expect(await groupCall.setLocalVideoMuted(false)).toBe(false); @@ -966,7 +966,10 @@ describe("Group Call", function () { // @ts-ignore const call = groupCall.calls.get(FAKE_USER_ID_2)!.get(FAKE_DEVICE_ID_2)!; - call.getOpponentMember = () => ({ userId: call.invitee }) as RoomMember; + call.getOpponentMember = () => + ({ + userId: call.invitee, + }) as RoomMember; // @ts-ignore Mock call.pushRemoteFeed( // @ts-ignore Mock @@ -993,7 +996,10 @@ describe("Group Call", function () { // @ts-ignore const call = groupCall.calls.get(FAKE_USER_ID_2).get(FAKE_DEVICE_ID_2)!; - call.getOpponentMember = () => ({ userId: call.invitee }) as RoomMember; + call.getOpponentMember = () => + ({ + userId: call.invitee, + }) as RoomMember; // @ts-ignore Mock call.pushRemoteFeed( // @ts-ignore Mock @@ -1106,7 +1112,7 @@ describe("Group Call", function () { }); const aliceEnters = () => { - room.currentState.getStateEvents = jest.fn().mockImplementation( + room.currentState.getStateEvents = vi.fn().mockImplementation( mockGetStateEvents([ { getContent: () => ({ @@ -1134,7 +1140,7 @@ describe("Group Call", function () { }; const aliceLeaves = () => { - room.currentState.getStateEvents = jest + room.currentState.getStateEvents = vi .fn() .mockImplementation(mockGetStateEvents([] as unknown as MatrixEvent[])); room.currentState.emit(RoomStateEvent.Update, room.currentState); @@ -1177,7 +1183,7 @@ describe("Group Call", function () { let newCallsMap: Map>; beforeEach(() => { - callChangedListener = jest.fn(); + callChangedListener = vi.fn(); groupCall.addListener(GroupCallEvent.CallsChanged, callChangedListener); oldMockCall = new MockMatrixCall(room.roomId, groupCall.groupCallId); @@ -1215,7 +1221,7 @@ describe("Group Call", function () { let mockCall: MockMatrixCall; beforeEach(() => { - callChangedListener = jest.fn(); + callChangedListener = vi.fn(); groupCall.addListener(GroupCallEvent.CallsChanged, callChangedListener); mockCall = new MockMatrixCall(room.roomId, groupCall.groupCallId); }); @@ -1267,7 +1273,7 @@ describe("Group Call", function () { userId: FAKE_USER_ID_2, membership: KnownMembership.Join, } as unknown as RoomMember; - room.currentState.getStateEvents = jest.fn().mockImplementation(mockGetStateEvents()); + room.currentState.getStateEvents = vi.fn().mockImplementation(mockGetStateEvents()); groupCall = await createAndEnterGroupCall(mockClient, room); }); @@ -1276,7 +1282,7 @@ describe("Group Call", function () { const onNegotiationNeededArray: (() => Promise)[] = []; groupCall.forEachCall((call) => { // @ts-ignore Mock - onNegotiationNeededArray.push((call.gotLocalOffer = jest.fn())); + onNegotiationNeededArray.push((call.gotLocalOffer = vi.fn())); }); let enabledResult: boolean; @@ -1311,7 +1317,10 @@ describe("Group Call", function () { // @ts-ignore const call = groupCall.calls.get(FAKE_USER_ID_2)!.get(FAKE_DEVICE_ID_2)!; - call.getOpponentMember = () => ({ userId: call.invitee }) as RoomMember; + call.getOpponentMember = () => + ({ + userId: call.invitee, + }) as RoomMember; call.onNegotiateReceived({ getContent: () => ({ [SDPStreamMetadataKey]: { @@ -1339,8 +1348,8 @@ describe("Group Call", function () { it("cleans up screensharing when terminating", async () => { // @ts-ignore Mock - jest.spyOn(groupCall, "removeScreenshareFeed"); - jest.spyOn(mockClient.getMediaHandler(), "stopScreensharingStream"); + vi.spyOn(groupCall, "removeScreenshareFeed"); + vi.spyOn(mockClient.getMediaHandler(), "stopScreensharingStream"); await groupCall.setScreensharingEnabled(true); @@ -1365,7 +1374,7 @@ describe("Group Call", function () { let onActiveSpeakerEvent: jest.Mock; beforeEach(async () => { - jest.useFakeTimers(); + vi.useFakeTimers(); const mockClient = new MockCallMatrixClient(FAKE_USER_ID_1, FAKE_DEVICE_ID_1, FAKE_SESSION_ID_1); @@ -1399,28 +1408,28 @@ describe("Group Call", function () { }); groupCall.userMediaFeeds.push(mediaFeed2); - onActiveSpeakerEvent = jest.fn(); + onActiveSpeakerEvent = vi.fn(); groupCall.on(GroupCallEvent.ActiveSpeakerChanged, onActiveSpeakerEvent); }); afterEach(() => { groupCall.off(GroupCallEvent.ActiveSpeakerChanged, onActiveSpeakerEvent); - jest.useRealTimers(); + vi.useRealTimers(); }); it("fires active speaker events when a user is speaking", async () => { mediaFeed1.speakingVolumeSamples = [100, 100]; mediaFeed2.speakingVolumeSamples = [0, 0]; - jest.runOnlyPendingTimers(); + vi.runOnlyPendingTimers(); expect(groupCall.activeSpeaker).toEqual(mediaFeed1); expect(onActiveSpeakerEvent).toHaveBeenCalledWith(mediaFeed1); mediaFeed1.speakingVolumeSamples = [0, 0]; mediaFeed2.speakingVolumeSamples = [100, 100]; - jest.runOnlyPendingTimers(); + vi.runOnlyPendingTimers(); expect(groupCall.activeSpeaker).toEqual(mediaFeed2); expect(onActiveSpeakerEvent).toHaveBeenCalledWith(mediaFeed2); }); @@ -1432,7 +1441,7 @@ describe("Group Call", function () { beforeEach(() => { client = new MatrixClient({ baseUrl: "base_url", userId: "my_user_id" }); - jest.spyOn(client, "sendStateEvent").mockResolvedValue({} as any); + vi.spyOn(client, "sendStateEvent").mockResolvedValue({} as any); }); afterEach(() => { @@ -1440,7 +1449,7 @@ describe("Group Call", function () { }); it("throws when there already is a call", async () => { - jest.spyOn(client, "getRoom").mockReturnValue(new Room("room_id", client, "my_user_id")); + vi.spyOn(client, "getRoom").mockReturnValue(new Room("room_id", client, "my_user_id")); await client.createGroupCall("room_id", GroupCallType.Video, false, GroupCallIntent.Prompt); @@ -1457,7 +1466,7 @@ describe("Group Call", function () { describe("correctly passes parameters", () => { beforeEach(() => { - jest.spyOn(client, "getRoom").mockReturnValue(new Room("room_id", client, "my_user_id")); + vi.spyOn(client, "getRoom").mockReturnValue(new Room("room_id", client, "my_user_id")); }); it("correctly passes voice ptt room call", async () => { @@ -1554,15 +1563,15 @@ describe("Group Call", function () { let groupCall: GroupCall; beforeAll(() => { - jest.useFakeTimers(); - jest.setSystemTime(0); + vi.useFakeTimers(); + vi.setSystemTime(0); }); - afterAll(() => jest.useRealTimers()); + afterAll(() => vi.useRealTimers()); beforeEach(async () => { const typedMockClient = new MockCallMatrixClient(FAKE_USER_ID_2, bobWeb.device_id, FAKE_SESSION_ID_2); - jest.spyOn(typedMockClient, "sendStateEvent").mockImplementation( + vi.spyOn(typedMockClient, "sendStateEvent").mockImplementation( async (roomId, eventType, content, stateKey) => { const eventId = `$${Math.random()}`; if (roomId === room.roomId) { @@ -1586,7 +1595,7 @@ describe("Group Call", function () { mockClient = typedMockClient as unknown as MatrixClient; room = new Room(FAKE_ROOM_ID, mockClient, FAKE_USER_ID_2); - room.getMember = jest.fn().mockImplementation((userId) => ({ userId })); + room.getMember = vi.fn().mockImplementation((userId) => ({ userId })); groupCall = new GroupCall( mockClient, @@ -1665,11 +1674,11 @@ describe("Group Call", function () { let groupCall: GroupCall; beforeAll(() => { - jest.useFakeTimers(); - jest.setSystemTime(0); + vi.useFakeTimers(); + vi.setSystemTime(0); }); - afterAll(() => jest.useRealTimers()); + afterAll(() => vi.useRealTimers()); beforeEach(async () => { const typedMockClient = new MockCallMatrixClient(FAKE_USER_ID_1, FAKE_DEVICE_ID_1, FAKE_SESSION_ID_1); @@ -1712,9 +1721,9 @@ describe("Group Call", function () { it("with number should stop existing stats", async () => { const stats = groupCall.getGroupCallStats(); // @ts-ignore - const stop = jest.spyOn(stats, "stop"); + const stop = vi.spyOn(stats, "stop"); // @ts-ignore - const start = jest.spyOn(stats, "start"); + const start = vi.spyOn(stats, "start"); groupCall.setGroupCallStatsInterval(0); expect(stop).toHaveBeenCalled(); @@ -1724,9 +1733,9 @@ describe("Group Call", function () { it("with number should restart existing stats", async () => { const stats = groupCall.getGroupCallStats(); // @ts-ignore - const stop = jest.spyOn(stats, "stop"); + const stop = vi.spyOn(stats, "stop"); // @ts-ignore - const start = jest.spyOn(stats, "start"); + const start = vi.spyOn(stats, "start"); groupCall.setGroupCallStatsInterval(10000); expect(stop).toHaveBeenCalled(); @@ -1739,7 +1748,7 @@ describe("Group Call", function () { let reportEmitter: StatsReportEmitter; const report: CallFeedReport = {} as CallFeedReport; beforeEach(async () => { - CallFeedStatsReporter.expandCallFeedReport = jest.fn().mockReturnValue(report); + CallFeedStatsReporter.expandCallFeedReport = vi.fn().mockReturnValue(report); const typedMockClient = new MockCallMatrixClient(FAKE_USER_ID_1, FAKE_DEVICE_ID_1, FAKE_SESSION_ID_1); const mockClient = typedMockClient.typed(); const room = new Room(FAKE_ROOM_ID, mockClient, FAKE_USER_ID_1); @@ -1751,7 +1760,7 @@ describe("Group Call", function () { userId: FAKE_USER_ID_2, membership: KnownMembership.Join, } as unknown as RoomMember; - room.currentState.getStateEvents = jest.fn().mockImplementation(mockGetStateEvents()); + room.currentState.getStateEvents = vi.fn().mockImplementation(mockGetStateEvents()); groupCall = await createAndEnterGroupCall(mockClient, room); reportEmitter = groupCall.getGroupCallStats().reports; }); diff --git a/spec/unit/webrtc/groupCallEventHandler.spec.ts b/spec/unit/webrtc/groupCallEventHandler.spec.ts index 850ba403787..0791d26c031 100644 --- a/spec/unit/webrtc/groupCallEventHandler.spec.ts +++ b/spec/unit/webrtc/groupCallEventHandler.spec.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { mocked } from "jest-mock"; +import { afterEach, beforeEach, describe, expect, it, vi, MockedFunction } from "vitest"; import { ClientEvent } from "../../../src/client"; import { RoomMember } from "../../../src/models/room-member"; @@ -61,7 +61,7 @@ describe("Group Call Event Handler", function () { off: () => {}, roomId: FAKE_ROOM_ID, currentState: { - getStateEvents: jest.fn((type, key) => { + getStateEvents: vi.fn((type, key) => { if (type === mockEvent.getType()) { return key === undefined ? [mockEvent] : mockEvent; } else { @@ -72,7 +72,7 @@ describe("Group Call Event Handler", function () { getMember: (userId: string) => (userId === FAKE_USER_ID ? mockMember : null), } as unknown as Room; - mockClient.getRoom = jest.fn().mockReturnValue(mockRoom); + mockClient.getRoom = vi.fn().mockReturnValue(mockRoom); mockClient.getFoci.mockReturnValue([{}]); }); @@ -145,7 +145,7 @@ describe("Group Call Event Handler", function () { }); it("finds existing group calls when started", async () => { - const mockClientEmit = (mockClient.emit = jest.fn()); + const mockClientEmit = (mockClient.emit = vi.fn()); mockClient.getRooms.mockReturnValue([mockRoom]); await groupCallEventHandler.start(); @@ -181,7 +181,7 @@ describe("Group Call Event Handler", function () { }); it("fires events for incoming calls", async () => { - const onIncomingGroupCall = jest.fn(); + const onIncomingGroupCall = vi.fn(); mockClient.on(GroupCallEventHandlerEvent.Incoming, onIncomingGroupCall); await groupCallEventHandler.start(); @@ -229,20 +229,20 @@ describe("Group Call Event Handler", function () { }); describe("ignoring invalid group call state events", () => { - let mockClientEmit: jest.Func; + let mockClientEmit: MockedFunction; beforeEach(() => { - mockClientEmit = mockClient.emit = jest.fn(); + mockClientEmit = mockClient.emit = vi.fn(); }); afterEach(() => { groupCallEventHandler.stop(); - jest.clearAllMocks(); + vi.clearAllMocks(); }); const setupCallAndStart = async (content?: IContent, redacted?: boolean) => { - mocked(mockRoom.currentState.getStateEvents).mockReturnValue([ + mockRoom.currentState.getStateEvents.mockReturnValue([ makeMockGroupCallStateEvent(FAKE_ROOM_ID, FAKE_GROUP_CALL_ID, content, redacted), ] as unknown as MatrixEvent); mockClient.getRooms.mockReturnValue([mockRoom]); diff --git a/spec/unit/webrtc/mediaHandler.spec.ts b/spec/unit/webrtc/mediaHandler.spec.ts index 4d23ead5f21..27015eb929b 100644 --- a/spec/unit/webrtc/mediaHandler.spec.ts +++ b/spec/unit/webrtc/mediaHandler.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + import { GroupCall, MatrixCall, MatrixClient } from "../../../src"; import { MediaHandler, MediaHandlerEvent } from "../../../src/webrtc/mediaHandler"; import { MockMediaDeviceInfo, MockMediaDevices, MockMediaStream, MockMediaStreamTrack } from "../../test-utils/webrtc"; @@ -135,7 +137,7 @@ describe("Media Handler", function () { let localStreamsChangedHandler: jest.Mock; beforeEach(() => { - localStreamsChangedHandler = jest.fn(); + localStreamsChangedHandler = vi.fn(); mediaHandler.on(MediaHandlerEvent.LocalStreamsChanged, localStreamsChangedHandler); }); @@ -159,7 +161,7 @@ describe("Media Handler", function () { let updateLocalUsermediaStream: jest.Mock; beforeEach(() => { - stopTrack = jest.fn(); + stopTrack = vi.fn(); mediaHandler.userMediaStreams = [ { @@ -171,7 +173,7 @@ describe("Media Handler", function () { } as unknown as MediaStream, ]; - updateLocalUsermediaStream = jest.fn(); + updateLocalUsermediaStream = vi.fn(); }); it("stops existing streams", async () => { @@ -183,7 +185,7 @@ describe("Media Handler", function () { calls.set("some_call", { hasLocalUserMediaAudioTrack: true, hasLocalUserMediaVideoTrack: true, - callHasEnded: jest.fn().mockReturnValue(false), + callHasEnded: vi.fn().mockReturnValue(false), updateLocalUsermediaStream, } as unknown as MatrixCall); @@ -195,7 +197,7 @@ describe("Media Handler", function () { calls.set("some_call", { hasLocalUserMediaAudioTrack: true, hasLocalUserMediaVideoTrack: true, - callHasEnded: jest.fn().mockReturnValue(true), + callHasEnded: vi.fn().mockReturnValue(true), updateLocalUsermediaStream, } as unknown as MatrixCall); @@ -371,7 +373,7 @@ describe("Media Handler", function () { }); it("emits LocalStreamsChanged", async () => { - const onLocalStreamChanged = jest.fn(); + const onLocalStreamChanged = vi.fn(); mediaHandler.on(MediaHandlerEvent.LocalStreamsChanged, onLocalStreamChanged); await mediaHandler.getScreensharingStream(); expect(onLocalStreamChanged).toHaveBeenCalled(); @@ -402,7 +404,7 @@ describe("Media Handler", function () { }); it("emits LocalStreamsChanged", async () => { - const onLocalStreamChanged = jest.fn(); + const onLocalStreamChanged = vi.fn(); mediaHandler.on(MediaHandlerEvent.LocalStreamsChanged, onLocalStreamChanged); mediaHandler.stopUserMediaStream(stream); expect(onLocalStreamChanged).toHaveBeenCalled(); @@ -433,7 +435,7 @@ describe("Media Handler", function () { }); it("emits LocalStreamsChanged", async () => { - const onLocalStreamChanged = jest.fn(); + const onLocalStreamChanged = vi.fn(); mediaHandler.on(MediaHandlerEvent.LocalStreamsChanged, onLocalStreamChanged); mediaHandler.stopScreensharingStream(stream); expect(onLocalStreamChanged).toHaveBeenCalled(); @@ -473,7 +475,7 @@ describe("Media Handler", function () { }); it("emits LocalStreamsChanged", async () => { - const onLocalStreamChanged = jest.fn(); + const onLocalStreamChanged = vi.fn(); mediaHandler.on(MediaHandlerEvent.LocalStreamsChanged, onLocalStreamChanged); mediaHandler.stopAllStreams(); expect(onLocalStreamChanged).toHaveBeenCalled(); diff --git a/spec/unit/webrtc/stats/__snapshots__/callFeedStatsReporter.spec.ts.snap b/spec/unit/webrtc/stats/__snapshots__/callFeedStatsReporter.spec.ts.snap index 54af0b4942f..da28b2b6c32 100644 --- a/spec/unit/webrtc/stats/__snapshots__/callFeedStatsReporter.spec.ts.snap +++ b/spec/unit/webrtc/stats/__snapshots__/callFeedStatsReporter.spec.ts.snap @@ -1,4 +1,113 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`CallFeedStatsReporter > should > builds CallFeedReport 1`] = ` +{ + "callFeeds": [], + "callId": "CALL_ID", + "opponentMemberId": "USER_ID", + "transceiver": [ + { + "currentDirection": "sendonly", + "direction": "sendrecv", + "mid": "0", + "receiver": { + "constrainDeviceId": "constrainDeviceId-receiver_audio_0", + "enabled": true, + "id": "receiver_audio_0", + "kind": "audio", + "label": "receiver", + "muted": false, + "readyState": "live", + "settingDeviceId": "settingDeviceId-receiver_audio_0", + }, + "sender": { + "constrainDeviceId": "constrainDeviceId-sender_audio_0", + "enabled": true, + "id": "sender_audio_0", + "kind": "audio", + "label": "sender", + "muted": false, + "readyState": "live", + "settingDeviceId": "settingDeviceId-sender_audio_0", + }, + }, + { + "currentDirection": "sendrecv", + "direction": "recvonly", + "mid": "1", + "receiver": { + "constrainDeviceId": "constrainDeviceId-receiver_video_1", + "enabled": true, + "id": "receiver_video_1", + "kind": "video", + "label": "receiver", + "muted": false, + "readyState": "live", + "settingDeviceId": "settingDeviceId-receiver_video_1", + }, + "sender": { + "constrainDeviceId": "constrainDeviceId-sender_video_1", + "enabled": true, + "id": "sender_video_1", + "kind": "video", + "label": "sender", + "muted": false, + "readyState": "live", + "settingDeviceId": "settingDeviceId-sender_video_1", + }, + }, + { + "currentDirection": "recvonly", + "direction": "recvonly", + "mid": "2", + "receiver": { + "constrainDeviceId": "constrainDeviceId-receiver_video_2", + "enabled": true, + "id": "receiver_video_2", + "kind": "video", + "label": "receiver", + "muted": false, + "readyState": "live", + "settingDeviceId": "settingDeviceId-receiver_video_2", + }, + "sender": null, + }, + ], +} +`; + +exports[`CallFeedStatsReporter > should > extends CallFeedReport with call feeds 1`] = ` +[ + { + "audio": { + "constrainDeviceId": "constrainDeviceId-video-1", + "enabled": true, + "id": "video-1", + "kind": "video", + "label": "--", + "muted": false, + "readyState": "live", + "settingDeviceId": "settingDeviceId-video-1", + }, + "isAudioMuted": true, + "isVideoMuted": false, + "prefix": "unknown", + "purpose": undefined, + "stream": "stream-1", + "type": "local", + "video": { + "constrainDeviceId": "constrainDeviceId-audio-1", + "enabled": true, + "id": "audio-1", + "kind": "audio", + "label": "--", + "muted": false, + "readyState": "live", + "settingDeviceId": "settingDeviceId-audio-1", + }, + }, +] +`; exports[`CallFeedStatsReporter should builds CallFeedReport 1`] = ` { diff --git a/spec/unit/webrtc/stats/callFeedStatsReporter.spec.ts b/spec/unit/webrtc/stats/callFeedStatsReporter.spec.ts index 32e43ec3e78..73783a974a1 100644 --- a/spec/unit/webrtc/stats/callFeedStatsReporter.spec.ts +++ b/spec/unit/webrtc/stats/callFeedStatsReporter.spec.ts @@ -13,6 +13,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ +import { beforeEach, describe, expect, it, vi } from "vitest"; + import { CallFeedStatsReporter } from "../../../../src/webrtc/stats/callFeedStatsReporter"; import { CallFeedReport } from "../../../../src/webrtc/stats/statsReport"; import { CallFeed } from "../../../../src/webrtc/callFeed"; @@ -23,7 +25,7 @@ describe("CallFeedStatsReporter", () => { let rtcSpy: RTCPeerConnection; beforeEach(() => { rtcSpy = {} as RTCPeerConnection; - rtcSpy.getTransceivers = jest.fn().mockReturnValue(buildTransceiverMocks()); + rtcSpy.getTransceivers = vi.fn().mockReturnValue(buildTransceiverMocks()); }); describe("should", () => { diff --git a/spec/unit/webrtc/stats/callStatsReportGatherer.spec.ts b/spec/unit/webrtc/stats/callStatsReportGatherer.spec.ts index 0c9c943cb15..03b6afd7f16 100644 --- a/spec/unit/webrtc/stats/callStatsReportGatherer.spec.ts +++ b/spec/unit/webrtc/stats/callStatsReportGatherer.spec.ts @@ -29,17 +29,17 @@ describe("CallStatsReportGatherer", () => { let emitter: StatsReportEmitter; beforeEach(() => { rtcSpy = { getStats: () => new Promise(() => null) } as RTCPeerConnection; - rtcSpy.addEventListener = jest.fn(); - rtcSpy.getTransceivers = jest.fn().mockReturnValue([]); + rtcSpy.addEventListener = vi.fn(); + rtcSpy.getTransceivers = vi.fn().mockReturnValue([]); emitter = new StatsReportEmitter(); collector = new CallStatsReportGatherer(CALL_ID, USER_ID, rtcSpy, emitter); }); describe("on process stats", () => { it("if active calculate stats reports", async () => { - const getStats = jest.spyOn(rtcSpy, "getStats"); + const getStats = vi.spyOn(rtcSpy, "getStats"); const report = {} as RTCStatsReport; - report.forEach = jest.fn().mockReturnValue([]); + report.forEach = vi.fn().mockReturnValue([]); getStats.mockResolvedValue(report); const actual = await collector.processStats("GROUP_CALL_ID", "LOCAL_USER_ID"); expect(getStats).toHaveBeenCalled(); @@ -70,14 +70,14 @@ describe("CallStatsReportGatherer", () => { it("if not active do not calculate stats reports", async () => { collector.setActive(false); - const getStats = jest.spyOn(rtcSpy, "getStats"); + const getStats = vi.spyOn(rtcSpy, "getStats"); await collector.processStats("GROUP_CALL_ID", "LOCAL_USER_ID"); expect(getStats).not.toHaveBeenCalled(); }); it("if get reports fails, the collector becomes inactive", async () => { expect(collector.getActive()).toBeTruthy(); - const getStats = jest.spyOn(rtcSpy, "getStats"); + const getStats = vi.spyOn(rtcSpy, "getStats"); getStats.mockRejectedValue(new Error("unknown")); await collector.processStats("GROUP_CALL_ID", "LOCAL_USER_ID"); expect(getStats).toHaveBeenCalled(); @@ -85,7 +85,7 @@ describe("CallStatsReportGatherer", () => { }); it("if active and getStats returns not an RTCStatsReport inside a promise the collector fails and becomes inactive", async () => { - const getStats = jest.spyOn(rtcSpy, "getStats"); + const getStats = vi.spyOn(rtcSpy, "getStats"); // @ts-ignore getStats.mockReturnValue({}); const actual = await collector.processStats("GROUP_CALL_ID", "LOCAL_USER_ID"); @@ -116,11 +116,11 @@ describe("CallStatsReportGatherer", () => { }); it("if active and the collector runs not the first time the Summery Stats is marked as not fits collection", async () => { - const getStats = jest.spyOn(rtcSpy, "getStats"); + const getStats = vi.spyOn(rtcSpy, "getStats"); // @ts-ignore collector.previousStatsReport = {} as RTCStatsReport; const report = {} as RTCStatsReport; - report.forEach = jest.fn().mockReturnValue([]); + report.forEach = vi.fn().mockReturnValue([]); getStats.mockResolvedValue(report); const actual = await collector.processStats("GROUP_CALL_ID", "LOCAL_USER_ID"); expect(getStats).toHaveBeenCalled(); @@ -175,7 +175,7 @@ describe("CallStatsReportGatherer", () => { beforeEach(() => { rtcSpy = new MockRTCPeerConnection() as unknown as RTCPeerConnection; collector = new CallStatsReportGatherer(CALL_ID, USER_ID, rtcSpy, emitter); - const getStats = jest.spyOn(rtcSpy, "getStats"); + const getStats = vi.spyOn(rtcSpy, "getStats"); const previous = prevChromeReport as unknown as RTCStatsReport; previous.get = (id: string) => { @@ -194,19 +194,19 @@ describe("CallStatsReportGatherer", () => { }); it("emit byteSentStatsReport", async () => { - const emitByteSendReport = jest.spyOn(emitter, "emitByteSendReport"); + const emitByteSendReport = vi.spyOn(emitter, "emitByteSendReport"); const actual = await collector.processStats("GROUP_CALL_ID", "LOCAL_USER_ID"); expect(actual).toEqual(wantedSummaryReport); expect(emitByteSendReport).toHaveBeenCalled(); }); it("emit emitConnectionStatsReport", async () => { - const emitConnectionStatsReport = jest.spyOn(emitter, "emitConnectionStatsReport"); + const emitConnectionStatsReport = vi.spyOn(emitter, "emitConnectionStatsReport"); const actual = await collector.processStats("GROUP_CALL_ID", "LOCAL_USER_ID"); expect(actual).toEqual(wantedSummaryReport); expect(emitConnectionStatsReport).toHaveBeenCalled(); }); it("emit callFeedStatsReport", async () => { - const emitCallFeedReport = jest.spyOn(emitter, "emitCallFeedReport"); + const emitCallFeedReport = vi.spyOn(emitter, "emitCallFeedReport"); const actual = await collector.processStats("GROUP_CALL_ID", "LOCAL_USER_ID"); expect(actual).toEqual(wantedSummaryReport); expect(emitCallFeedReport).toHaveBeenCalled(); @@ -219,7 +219,7 @@ describe("CallStatsReportGatherer", () => { beforeEach(() => { events = []; // Define the addEventListener method with a Jest mock function - rtcSpy.addEventListener = jest.fn((event: any, callback: any) => { + rtcSpy.addEventListener = vi.fn((event: any, callback: any) => { events[event] = callback; }); @@ -228,10 +228,10 @@ describe("CallStatsReportGatherer", () => { it("in case of stable, parse remote and local description", () => { // @ts-ignore const mediaSsrcHandler = { - parse: jest.fn(), - ssrcToMid: jest.fn(), - findMidBySsrc: jest.fn(), - getSsrcToMidMap: jest.fn(), + parse: vi.fn(), + ssrcToMid: vi.fn(), + findMidBySsrc: vi.fn(), + getSsrcToMidMap: vi.fn(), } as MediaSsrcHandler; const remoteSDP = "sdp"; diff --git a/spec/unit/webrtc/stats/connectionStatsReportBuilder.spec.ts b/spec/unit/webrtc/stats/connectionStatsReportBuilder.spec.ts index d8fb9a756f9..37cb98c4f73 100644 --- a/spec/unit/webrtc/stats/connectionStatsReportBuilder.spec.ts +++ b/spec/unit/webrtc/stats/connectionStatsReportBuilder.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { beforeEach, describe, expect, it } from "vitest"; + import { TrackID } from "../../../../src/webrtc/stats/statsReport"; import { MediaTrackStats } from "../../../../src/webrtc/stats/media/mediaTrackStats"; import { ConnectionStatsReportBuilder } from "../../../../src/webrtc/stats/connectionStatsReportBuilder"; diff --git a/spec/unit/webrtc/stats/connectionStatsReporter.spec.ts b/spec/unit/webrtc/stats/connectionStatsReporter.spec.ts index 7356f2576ab..f5e1e6e3d48 100644 --- a/spec/unit/webrtc/stats/connectionStatsReporter.spec.ts +++ b/spec/unit/webrtc/stats/connectionStatsReporter.spec.ts @@ -13,6 +13,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ +import { describe, expect, it } from "vitest"; + import { ConnectionStatsBuilder } from "../../../../src/webrtc/stats/connectionStatsBuilder"; describe("ConnectionStatsReporter", () => { diff --git a/spec/unit/webrtc/stats/groupCallStats.spec.ts b/spec/unit/webrtc/stats/groupCallStats.spec.ts index ae97148764f..30927d355b9 100644 --- a/spec/unit/webrtc/stats/groupCallStats.spec.ts +++ b/spec/unit/webrtc/stats/groupCallStats.spec.ts @@ -13,6 +13,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + import { GroupCallStats } from "../../../../src/webrtc/stats/groupCallStats"; import { CallStatsReportSummary } from "../../../../src/webrtc/stats/callStatsReportSummary"; @@ -62,17 +64,17 @@ describe("GroupCallStats", () => { describe("should on start", () => { beforeEach(() => { - jest.useFakeTimers(); + vi.useFakeTimers(); }); afterEach(() => { - jest.useRealTimers(); + vi.useRealTimers(); }); it("starting processing stats as well without stats collectors", async () => { // @ts-ignore - stats.processStats = jest.fn(); + stats.processStats = vi.fn(); stats.start(); - jest.advanceTimersByTime(TIME_INTERVAL); + vi.advanceTimersByTime(TIME_INTERVAL); // @ts-ignore expect(stats.processStats).toHaveBeenCalled(); }); @@ -80,9 +82,9 @@ describe("GroupCallStats", () => { it("not starting processing stats if interval 0", async () => { const statsDisabled = new GroupCallStats(GROUP_CALL_ID, LOCAL_USER_ID, 0); // @ts-ignore - statsDisabled.processStats = jest.fn(); + statsDisabled.processStats = vi.fn(); statsDisabled.start(); - jest.advanceTimersByTime(TIME_INTERVAL); + vi.advanceTimersByTime(TIME_INTERVAL); // @ts-ignore expect(statsDisabled.processStats).not.toHaveBeenCalled(); }); @@ -90,7 +92,7 @@ describe("GroupCallStats", () => { it("starting processing and calling the collectors", async () => { stats.addStatsReportGatherer("CALL_ID", "USER_ID", mockRTCPeerConnection()); const collector = stats.getStatsReportGatherer("CALL_ID"); - stats.reports.emitSummaryStatsReport = jest.fn(); + stats.reports.emitSummaryStatsReport = vi.fn(); const summaryStats = { isFirstCollection: true, receivedMedia: 0, @@ -115,9 +117,9 @@ describe("GroupCallStats", () => { } as CallStatsReportSummary; let processStatsSpy; if (collector) { - processStatsSpy = jest.spyOn(collector, "processStats").mockResolvedValue(summaryStats); + processStatsSpy = vi.spyOn(collector, "processStats").mockResolvedValue(summaryStats); stats.start(); - jest.advanceTimersByTime(TIME_INTERVAL); + vi.advanceTimersByTime(TIME_INTERVAL); } else { throw new Error("Test failed, because no Collector found!"); } @@ -126,7 +128,7 @@ describe("GroupCallStats", () => { it("doing nothing if process already running", async () => { // @ts-ignore - jest.spyOn(globalThis, "setInterval").mockReturnValue(22); + vi.spyOn(globalThis, "setInterval").mockReturnValue(22); stats.start(); expect(setInterval).toHaveBeenCalledTimes(1); stats.start(); @@ -139,15 +141,15 @@ describe("GroupCallStats", () => { describe("should on stop", () => { beforeEach(() => { - jest.useFakeTimers(); + vi.useFakeTimers(); }); afterEach(() => { - jest.useRealTimers(); + vi.useRealTimers(); }); it("finish stats process if was started", async () => { // @ts-ignore - jest.spyOn(globalThis, "setInterval").mockReturnValue(22); - jest.spyOn(globalThis, "clearInterval"); + vi.spyOn(globalThis, "setInterval").mockReturnValue(22); + vi.spyOn(globalThis, "clearInterval"); stats.start(); expect(setInterval).toHaveBeenCalledTimes(1); stats.stop(); @@ -155,7 +157,7 @@ describe("GroupCallStats", () => { }); it("do nothing if stats process was not started", async () => { - jest.spyOn(globalThis, "clearInterval"); + vi.spyOn(globalThis, "clearInterval"); stats.stop(); expect(clearInterval).not.toHaveBeenCalled(); }); @@ -164,7 +166,7 @@ describe("GroupCallStats", () => { const mockRTCPeerConnection = (): RTCPeerConnection => { const pc = {} as RTCPeerConnection; - pc.addEventListener = jest.fn(); - pc.getStats = jest.fn().mockResolvedValue(null); + pc.addEventListener = vi.fn(); + pc.getStats = vi.fn().mockResolvedValue(null); return pc; }; diff --git a/spec/unit/webrtc/stats/media/mediaSsrcHandler.spec.ts b/spec/unit/webrtc/stats/media/mediaSsrcHandler.spec.ts index 3a252afdb8e..f8868590060 100644 --- a/spec/unit/webrtc/stats/media/mediaSsrcHandler.spec.ts +++ b/spec/unit/webrtc/stats/media/mediaSsrcHandler.spec.ts @@ -13,6 +13,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ +import { beforeEach, describe, expect, it } from "vitest"; + import { Mid, Ssrc, MediaSsrcHandler } from "../../../../../src/webrtc/stats/media/mediaSsrcHandler"; import { REMOTE_SFU_DESCRIPTION } from "../../../../test-utils/webrtc"; diff --git a/spec/unit/webrtc/stats/media/mediaTrackHandler.spec.ts b/spec/unit/webrtc/stats/media/mediaTrackHandler.spec.ts index 35bd04eaca1..008a2761482 100644 --- a/spec/unit/webrtc/stats/media/mediaTrackHandler.spec.ts +++ b/spec/unit/webrtc/stats/media/mediaTrackHandler.spec.ts @@ -13,6 +13,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ +import { beforeEach, describe, expect, it } from "vitest"; + import { MediaTrackHandler } from "../../../../../src/webrtc/stats/media/mediaTrackHandler"; describe("TrackHandler", () => { diff --git a/spec/unit/webrtc/stats/media/mediaTrackStatsHandler.spec.ts b/spec/unit/webrtc/stats/media/mediaTrackStatsHandler.spec.ts index 85fcc61510d..f7c0dd53443 100644 --- a/spec/unit/webrtc/stats/media/mediaTrackStatsHandler.spec.ts +++ b/spec/unit/webrtc/stats/media/mediaTrackStatsHandler.spec.ts @@ -13,6 +13,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ +import { beforeEach, describe, expect, it, vi } from "vitest"; + import { MediaTrackHandler } from "../../../../../src/webrtc/stats/media/mediaTrackHandler"; import { MediaTrackStatsHandler } from "../../../../../src/webrtc/stats/media/mediaTrackStatsHandler"; import { MediaSsrcHandler } from "../../../../../src/webrtc/stats/media/mediaSsrcHandler"; @@ -24,10 +26,10 @@ describe("MediaTrackStatsHandler", () => { beforeEach(() => { ssrcHandler = {} as MediaSsrcHandler; trackHandler = {} as MediaTrackHandler; - trackHandler.getLocalTrackIdByMid = jest.fn().mockReturnValue("2222"); - trackHandler.getRemoteTrackIdByMid = jest.fn().mockReturnValue("5555"); - trackHandler.getLocalTracks = jest.fn().mockReturnValue([{ id: "2222" } as MediaStreamTrack]); - trackHandler.getTackById = jest.fn().mockReturnValue([{ id: "2222", kind: "audio" } as MediaStreamTrack]); + trackHandler.getLocalTrackIdByMid = vi.fn().mockReturnValue("2222"); + trackHandler.getRemoteTrackIdByMid = vi.fn().mockReturnValue("5555"); + trackHandler.getLocalTracks = vi.fn().mockReturnValue([{ id: "2222" } as MediaStreamTrack]); + trackHandler.getTackById = vi.fn().mockReturnValue([{ id: "2222", kind: "audio" } as MediaStreamTrack]); statsHandler = new MediaTrackStatsHandler(ssrcHandler, trackHandler); }); describe("should find track stats", () => { @@ -43,17 +45,17 @@ describe("MediaTrackStatsHandler", () => { }); it("and returns undefined if `ssrc` exists in report but not on connection", () => { const report = { ssrc: "142443", type: "inbound-rtp" }; - ssrcHandler.findMidBySsrc = jest.fn().mockReturnValue(undefined); + ssrcHandler.findMidBySsrc = vi.fn().mockReturnValue(undefined); expect(statsHandler.findTrack2Stats(report, "local")?.trackId).toBeUndefined(); }); it("and returns undefined if `ssrc` exists in inbound-rtp report", () => { const report = { ssrc: "142443", type: "inbound-rtp" }; - ssrcHandler.findMidBySsrc = jest.fn().mockReturnValue("2"); + ssrcHandler.findMidBySsrc = vi.fn().mockReturnValue("2"); expect(statsHandler.findTrack2Stats(report, "remote")?.trackId).toEqual("5555"); }); it("and returns undefined if `ssrc` exists in outbound-rtp report", () => { const report = { ssrc: "142443", type: "outbound-rtp" }; - ssrcHandler.findMidBySsrc = jest.fn().mockReturnValue("2"); + ssrcHandler.findMidBySsrc = vi.fn().mockReturnValue("2"); expect(statsHandler.findTrack2Stats(report, "local")?.trackId).toEqual("2222"); }); it("and returns undefined if needed property not existing", () => { @@ -72,7 +74,7 @@ describe("MediaTrackStatsHandler", () => { }); it("and returns undefined if `ssrc` exists", () => { const report = { ssrc: "142443", type: "outbound-rtp" }; - ssrcHandler.findMidBySsrc = jest.fn().mockReturnValue("2"); + ssrcHandler.findMidBySsrc = vi.fn().mockReturnValue("2"); expect(statsHandler.findTrack2Stats(report, "local")?.trackId).toEqual("2222"); }); it("and returns undefined if needed property not existing", async () => { @@ -83,13 +85,13 @@ describe("MediaTrackStatsHandler", () => { describe("should find a Transceiver by Track id", () => { it("and returns undefined if Transceiver not existing", async () => { - trackHandler.getTransceiverByTrackId = jest.fn().mockReturnValue(undefined); + trackHandler.getTransceiverByTrackId = vi.fn().mockReturnValue(undefined); expect(statsHandler.findTransceiverByTrackId("12")).toBeUndefined(); }); it("and returns Transceiver if existing", async () => { const ts = {} as RTCRtpTransceiver; - trackHandler.getTransceiverByTrackId = jest.fn().mockReturnValue(ts); + trackHandler.getTransceiverByTrackId = vi.fn().mockReturnValue(ts); expect(statsHandler.findTransceiverByTrackId("12")).toEqual(ts); }); }); diff --git a/spec/unit/webrtc/stats/statsReportEmitter.spec.ts b/spec/unit/webrtc/stats/statsReportEmitter.spec.ts index c5237fd192f..3cae0a0532d 100644 --- a/spec/unit/webrtc/stats/statsReportEmitter.spec.ts +++ b/spec/unit/webrtc/stats/statsReportEmitter.spec.ts @@ -13,6 +13,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ +import { beforeEach, describe, expect, it } from "vitest"; + import { StatsReportEmitter } from "../../../../src/webrtc/stats/statsReportEmitter"; import { ByteSentStatsReport, diff --git a/spec/unit/webrtc/stats/summaryStatsReportGatherer.spec.ts b/spec/unit/webrtc/stats/summaryStatsReportGatherer.spec.ts index aca482e219c..8ce8f06add9 100644 --- a/spec/unit/webrtc/stats/summaryStatsReportGatherer.spec.ts +++ b/spec/unit/webrtc/stats/summaryStatsReportGatherer.spec.ts @@ -13,6 +13,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ +import { beforeEach, describe, expect, it, vi } from "vitest"; + import { SummaryStatsReportGatherer } from "../../../../src/webrtc/stats/summaryStatsReportGatherer"; import { StatsReportEmitter } from "../../../../src/webrtc/stats/statsReportEmitter"; import { groupCallParticipantsFourOtherDevices } from "../../../test-utils/webrtc"; @@ -22,7 +24,7 @@ describe("SummaryStatsReportGatherer", () => { let emitter: StatsReportEmitter; beforeEach(() => { emitter = new StatsReportEmitter(); - emitter.emitSummaryStatsReport = jest.fn(); + emitter.emitSummaryStatsReport = vi.fn(); reporter = new SummaryStatsReportGatherer(emitter); }); diff --git a/spec/unit/webrtc/stats/trackStatsBuilder.spec.ts b/spec/unit/webrtc/stats/trackStatsBuilder.spec.ts index 9bfe1169cdf..7cab76dff6c 100644 --- a/spec/unit/webrtc/stats/trackStatsBuilder.spec.ts +++ b/spec/unit/webrtc/stats/trackStatsBuilder.spec.ts @@ -13,6 +13,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ +import { describe, expect, it, vi } from "vitest"; + import { TrackStatsBuilder } from "../../../../src/webrtc/stats/trackStatsBuilder"; import { MediaTrackStats } from "../../../../src/webrtc/stats/media/mediaTrackStats"; @@ -89,7 +91,7 @@ describe("TrackStatsBuilder", () => { it("creating build bitrate send report.", async () => { const trackStats = new MediaTrackStats("1", "remote", "video"); const remote = {} as RTCStatsReport; - remote.get = jest.fn().mockReturnValue({ mimeType: "video/v8" }); + remote.get = vi.fn().mockReturnValue({ mimeType: "video/v8" }); TrackStatsBuilder.buildCodec(remote, trackStats, { codecId: "codecID" }); expect(trackStats.getCodec()).toEqual("v8"); }); diff --git a/spec/unit/webrtc/stats/transportStatsBuilder.spec.ts b/spec/unit/webrtc/stats/transportStatsBuilder.spec.ts index 17283f31e90..6f126bfcb82 100644 --- a/spec/unit/webrtc/stats/transportStatsBuilder.spec.ts +++ b/spec/unit/webrtc/stats/transportStatsBuilder.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { describe, expect, it } from "vitest"; + import { TransportStatsBuilder } from "../../../../src/webrtc/stats/transportStatsBuilder"; import { TransportStats } from "../../../../src/webrtc/stats/transportStats"; diff --git a/spec/unit/webrtc/stats/valueFormatter.spec.ts b/spec/unit/webrtc/stats/valueFormatter.spec.ts index 5009960b195..ea53d4dd5d1 100644 --- a/spec/unit/webrtc/stats/valueFormatter.spec.ts +++ b/spec/unit/webrtc/stats/valueFormatter.spec.ts @@ -13,6 +13,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ +import { describe, expect, it } from "vitest"; + import { ValueFormatter } from "../../../../src/webrtc/stats/valueFormatter"; describe("ValueFormatter", () => { diff --git a/src/webrtc/groupCall.ts b/src/webrtc/groupCall.ts index b4ecac79a3d..d3d7ae2bf2c 100644 --- a/src/webrtc/groupCall.ts +++ b/src/webrtc/groupCall.ts @@ -1006,9 +1006,8 @@ export class GroupCall extends TypedEventEmitter< const localDeviceId = this.client.getDeviceId()!; return ( // If a user's ID is less than our own, they'll call us - userId >= localUserId && - // If this is another one of our devices, compare device IDs to tell whether it'll call us - (userId !== localUserId || deviceId > localDeviceId) + (// If this is another one of our devices, compare device IDs to tell whether it'll call us + userId >= localUserId && (userId !== localUserId || deviceId > localDeviceId)) ); } diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 00000000000..18eac9039e4 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,20 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + coverage: { + provider: "v8", + include: ["spec/**/*"], + reporter: "lcov", + }, + reporters: [["vitest-sonar-reporter", { outputFile: "coverage/sonar-report.xml" }]], + setupFiles: "spec/setupTests.ts", + }, +}); diff --git a/yarn.lock b/yarn.lock index 7dce9dd08f6..3726571d17c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22,6 +22,17 @@ "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.24" +"@asamuzakjp/css-color@^2.8.2": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@asamuzakjp/css-color/-/css-color-2.8.3.tgz#665f0f5e8edb95d8f543847529e30fe5cc437ef7" + integrity sha512-GIc76d9UI1hCvOATjZPyHFmE5qhRccp3/zGfMPapK3jBi+yocEzp6BBB0UnfRYP9NP4FANqUZYb0hnfs3TM3hw== + dependencies: + "@csstools/css-calc" "^2.1.1" + "@csstools/css-color-parser" "^3.0.7" + "@csstools/css-parser-algorithms" "^3.0.4" + "@csstools/css-tokenizer" "^3.0.3" + lru-cache "^10.4.3" + "@babel/cli@^7.12.10": version "7.26.4" resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.26.4.tgz#4101ff8ee5de8447a6c395397a97921056411d20" @@ -38,7 +49,7 @@ "@nicolo-ribaudo/chokidar-2" "2.1.8-no-fsevents.3" chokidar "^3.6.0" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0", "@babel/code-frame@^7.26.2": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.2": version "7.26.2" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== @@ -47,7 +58,7 @@ js-tokens "^4.0.0" picocolors "^1.0.0" -"@babel/code-frame@^7.12.13", "@babel/code-frame@^7.24.7": +"@babel/code-frame@^7.12.13": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465" integrity sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA== @@ -60,27 +71,6 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.5.tgz#df93ac37f4417854130e21d72c66ff3d4b897fc7" integrity sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg== -"@babel/core@^7.0.0", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.0.tgz#d78b6023cc8f3114ccf049eb219613f74a747b40" - integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg== - dependencies: - "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.26.0" - "@babel/generator" "^7.26.0" - "@babel/helper-compilation-targets" "^7.25.9" - "@babel/helper-module-transforms" "^7.26.0" - "@babel/helpers" "^7.26.0" - "@babel/parser" "^7.26.0" - "@babel/template" "^7.25.9" - "@babel/traverse" "^7.25.9" - "@babel/types" "^7.26.0" - convert-source-map "^2.0.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.3" - semver "^6.3.1" - "@babel/core@^7.12.10": version "7.26.7" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.7.tgz#0439347a183b97534d52811144d763a17f9d2b24" @@ -118,7 +108,7 @@ dependencies: eslint-rule-composer "^0.3.0" -"@babel/generator@^7.26.0", "@babel/generator@^7.26.5": +"@babel/generator@^7.26.5": version "7.26.5" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.5.tgz#e44d4ab3176bbcaf78a5725da5f1dc28802a9458" integrity sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw== @@ -129,16 +119,6 @@ "@jridgewell/trace-mapping" "^0.3.25" jsesc "^3.0.2" -"@babel/generator@^7.7.2": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.24.7.tgz#1654d01de20ad66b4b4d99c135471bc654c55e6d" - integrity sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA== - dependencies: - "@babel/types" "^7.24.7" - "@jridgewell/gen-mapping" "^0.3.5" - "@jridgewell/trace-mapping" "^0.3.25" - jsesc "^2.5.1" - "@babel/helper-annotate-as-pure@^7.25.9": version "7.25.9" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz#d8eac4d2dc0d7b6e11fa6e535332e0d3184f06b4" @@ -227,7 +207,7 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz#18580d00c9934117ad719392c4f6585c9333cc35" integrity sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg== -"@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.24.7", "@babel/helper-plugin-utils@^7.8.0": +"@babel/helper-plugin-utils@^7.8.0": version "7.24.8" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz#94ee67e8ec0e5d44ea7baeb51e571bd26af07878" integrity sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg== @@ -258,22 +238,12 @@ "@babel/traverse" "^7.25.9" "@babel/types" "^7.25.9" -"@babel/helper-string-parser@^7.24.7": - version "7.24.8" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz#5b3329c9a58803d5df425e5785865881a81ca48d" - integrity sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ== - -"@babel/helper-string-parser@^7.24.8": - version "7.25.7" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz#d50e8d37b1176207b4fe9acedec386c565a44a54" - integrity sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g== - -"@babel/helper-string-parser@^7.25.7", "@babel/helper-string-parser@^7.25.9": +"@babel/helper-string-parser@^7.25.9": version "7.25.9" resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== -"@babel/helper-validator-identifier@^7.24.7", "@babel/helper-validator-identifier@^7.25.7", "@babel/helper-validator-identifier@^7.25.9": +"@babel/helper-validator-identifier@^7.24.7", "@babel/helper-validator-identifier@^7.25.9": version "7.25.9" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== @@ -292,7 +262,7 @@ "@babel/traverse" "^7.25.9" "@babel/types" "^7.25.9" -"@babel/helpers@^7.26.0", "@babel/helpers@^7.26.7": +"@babel/helpers@^7.26.7": version "7.26.7" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.7.tgz#fd1d2a7c431b6e39290277aacfd8367857c576a4" integrity sha512-8NHiL98vsi0mbPQmYAGWwfcFaOy4j2HY49fXJCfuDcdE7fMIsH9a7GdaeXpIBsbT7307WU8KCMp5pUVDNL4f9A== @@ -310,19 +280,7 @@ js-tokens "^4.0.0" picocolors "^1.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.7.tgz#9a5226f92f0c5c8ead550b750f5608e766c8ce85" - integrity sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw== - -"@babel/parser@^7.24.7": - version "7.25.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.25.3.tgz#91fb126768d944966263f0657ab222a642b82065" - integrity sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw== - dependencies: - "@babel/types" "^7.25.2" - -"@babel/parser@^7.25.9", "@babel/parser@^7.26.0", "@babel/parser@^7.26.5", "@babel/parser@^7.26.7": +"@babel/parser@^7.25.9", "@babel/parser@^7.26.5", "@babel/parser@^7.26.7": version "7.26.7" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.7.tgz#e114cd099e5f7d17b05368678da0fb9f69b3385c" integrity sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w== @@ -373,27 +331,6 @@ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703" integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== -"@babel/plugin-syntax-async-generators@^7.8.4": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" - integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-bigint@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" - integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-class-properties@^7.8.3": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" - integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - "@babel/plugin-syntax-dynamic-import@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" @@ -415,20 +352,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-syntax-import-meta@^7.8.3": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" - integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-json-strings@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" - integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - "@babel/plugin-syntax-jsx@^7.25.9": version "7.25.9" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz#a34313a178ea56f1951599b929c1ceacee719290" @@ -436,62 +359,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-syntax-jsx@^7.7.2": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz#39a1fa4a7e3d3d7f34e2acc6be585b718d30e02d" - integrity sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ== - dependencies: - "@babel/helper-plugin-utils" "^7.24.7" - -"@babel/plugin-syntax-logical-assignment-operators@^7.8.3": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" - integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" - integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-numeric-separator@^7.8.3": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" - integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-object-rest-spread@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" - integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-catch-binding@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" - integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-chaining@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" - integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-top-level-await@^7.8.3": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" - integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-typescript@^7.25.9": version "7.25.9" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz#67dda2b74da43727cf21d46cf9afef23f4365399" @@ -499,13 +366,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-syntax-typescript@^7.7.2": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.7.tgz#58d458271b4d3b6bb27ee6ac9525acbb259bad1c" - integrity sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA== - dependencies: - "@babel/helper-plugin-utils" "^7.24.7" - "@babel/plugin-syntax-unicode-sets-regex@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz#d49a3b3e6b52e5be6740022317580234a6a47357" @@ -1019,13 +879,6 @@ "@babel/plugin-transform-modules-commonjs" "^7.25.9" "@babel/plugin-transform-typescript" "^7.25.9" -"@babel/runtime@^7.0.0": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1" - integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw== - dependencies: - regenerator-runtime "^0.14.0" - "@babel/runtime@^7.12.5", "@babel/runtime@^7.8.4": version "7.26.7" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.7.tgz#f4e7fe527cd710f8dc0618610b61b4b060c3c341" @@ -1042,15 +895,6 @@ "@babel/parser" "^7.25.9" "@babel/types" "^7.25.9" -"@babel/template@^7.3.3": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.24.7.tgz#02efcee317d0609d2c07117cb70ef8fb17ab7315" - integrity sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig== - dependencies: - "@babel/code-frame" "^7.24.7" - "@babel/parser" "^7.24.7" - "@babel/types" "^7.24.7" - "@babel/traverse@^7.25.9", "@babel/traverse@^7.26.5", "@babel/traverse@^7.26.7": version "7.26.7" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.7.tgz#99a0a136f6a75e7fb8b0a1ace421e0b25994b8bb" @@ -1064,34 +908,7 @@ debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.3.3": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.7.tgz#6027fe12bc1aa724cd32ab113fb7f1988f1f66f2" - integrity sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q== - dependencies: - "@babel/helper-string-parser" "^7.24.7" - "@babel/helper-validator-identifier" "^7.24.7" - to-fast-properties "^2.0.0" - -"@babel/types@^7.24.7": - version "7.25.8" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.25.8.tgz#5cf6037258e8a9bcad533f4979025140cb9993e1" - integrity sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg== - dependencies: - "@babel/helper-string-parser" "^7.25.7" - "@babel/helper-validator-identifier" "^7.25.7" - to-fast-properties "^2.0.0" - -"@babel/types@^7.25.2": - version "7.25.6" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.25.6.tgz#893942ddb858f32ae7a004ec9d3a76b3463ef8e6" - integrity sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw== - dependencies: - "@babel/helper-string-parser" "^7.24.8" - "@babel/helper-validator-identifier" "^7.24.7" - to-fast-properties "^2.0.0" - -"@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.26.5", "@babel/types@^7.26.7", "@babel/types@^7.4.4": +"@babel/types@^7.25.9", "@babel/types@^7.26.5", "@babel/types@^7.26.7", "@babel/types@^7.4.4": version "7.26.7" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.7.tgz#5e2b89c0768e874d4d061961f3a5a153d71dc17a" integrity sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg== @@ -1099,20 +916,6 @@ "@babel/helper-string-parser" "^7.25.9" "@babel/helper-validator-identifier" "^7.25.9" -"@bcoe/v8-coverage@^0.2.3": - version "0.2.3" - resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" - integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== - -"@casualbot/jest-sonar-reporter@2.2.7": - version "2.2.7" - resolved "https://registry.yarnpkg.com/@casualbot/jest-sonar-reporter/-/jest-sonar-reporter-2.2.7.tgz#3cc14c64f5d8ab5e9163b03b9cd2e07456432ed0" - integrity sha512-iswhPNodtcOQzfXR3TkD0A/8yHr5fnC86Ryt5UAqrJWfMI8mPZ9mpjykHnibbf91SjNtELv7ApZtha0bdWOmoQ== - dependencies: - mkdirp "1.0.4" - uuid "8.3.2" - xml "1.0.1" - "@cspotcode/source-map-support@^0.8.0": version "0.8.1" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" @@ -1120,6 +923,34 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" +"@csstools/color-helpers@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@csstools/color-helpers/-/color-helpers-5.0.1.tgz#829f1c76f5800b79c51c709e2f36821b728e0e10" + integrity sha512-MKtmkA0BX87PKaO1NFRTFH+UnkgnmySQOvNxJubsadusqPEC2aJ9MOQiMceZJJ6oitUl/i0L6u0M1IrmAOmgBA== + +"@csstools/css-calc@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@csstools/css-calc/-/css-calc-2.1.1.tgz#a7dbc66627f5cf458d42aed14bda0d3860562383" + integrity sha512-rL7kaUnTkL9K+Cvo2pnCieqNpTKgQzy5f+N+5Iuko9HAoasP+xgprVh7KN/MaJVvVL1l0EzQq2MoqBHKSrDrag== + +"@csstools/css-color-parser@^3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@csstools/css-color-parser/-/css-color-parser-3.0.7.tgz#442d61d58e54ad258d52c309a787fceb33906484" + integrity sha512-nkMp2mTICw32uE5NN+EsJ4f5N+IGFeCFu4bGpiKgb2Pq/7J/MpyLBeQ5ry4KKtRFZaYs6sTmcMYrSRIyj5DFKA== + dependencies: + "@csstools/color-helpers" "^5.0.1" + "@csstools/css-calc" "^2.1.1" + +"@csstools/css-parser-algorithms@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz#74426e93bd1c4dcab3e441f5cc7ba4fb35d94356" + integrity sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A== + +"@csstools/css-tokenizer@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz#a5502c8539265fecbd873c1e395a890339f119c2" + integrity sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw== + "@es-joy/jsdoccomment@~0.49.0": version "0.49.0" resolved "https://registry.yarnpkg.com/@es-joy/jsdoccomment/-/jsdoccomment-0.49.0.tgz#e5ec1eda837c802eca67d3b29e577197f14ba1db" @@ -1129,6 +960,131 @@ esquery "^1.6.0" jsdoc-type-pratt-parser "~4.1.0" +"@esbuild/aix-ppc64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz#38848d3e25afe842a7943643cbcd387cc6e13461" + integrity sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA== + +"@esbuild/android-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz#f592957ae8b5643129fa889c79e69cd8669bb894" + integrity sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg== + +"@esbuild/android-arm@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.24.2.tgz#72d8a2063aa630308af486a7e5cbcd1e134335b3" + integrity sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q== + +"@esbuild/android-x64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.24.2.tgz#9a7713504d5f04792f33be9c197a882b2d88febb" + integrity sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw== + +"@esbuild/darwin-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz#02ae04ad8ebffd6e2ea096181b3366816b2b5936" + integrity sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA== + +"@esbuild/darwin-x64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz#9ec312bc29c60e1b6cecadc82bd504d8adaa19e9" + integrity sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA== + +"@esbuild/freebsd-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz#5e82f44cb4906d6aebf24497d6a068cfc152fa00" + integrity sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg== + +"@esbuild/freebsd-x64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz#3fb1ce92f276168b75074b4e51aa0d8141ecce7f" + integrity sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q== + +"@esbuild/linux-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz#856b632d79eb80aec0864381efd29de8fd0b1f43" + integrity sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg== + +"@esbuild/linux-arm@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz#c846b4694dc5a75d1444f52257ccc5659021b736" + integrity sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA== + +"@esbuild/linux-ia32@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz#f8a16615a78826ccbb6566fab9a9606cfd4a37d5" + integrity sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw== + +"@esbuild/linux-loong64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz#1c451538c765bf14913512c76ed8a351e18b09fc" + integrity sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ== + +"@esbuild/linux-mips64el@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz#0846edeefbc3d8d50645c51869cc64401d9239cb" + integrity sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw== + +"@esbuild/linux-ppc64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz#8e3fc54505671d193337a36dfd4c1a23b8a41412" + integrity sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw== + +"@esbuild/linux-riscv64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz#6a1e92096d5e68f7bb10a0d64bb5b6d1daf9a694" + integrity sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q== + +"@esbuild/linux-s390x@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz#ab18e56e66f7a3c49cb97d337cd0a6fea28a8577" + integrity sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw== + +"@esbuild/linux-x64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz#8140c9b40da634d380b0b29c837a0b4267aff38f" + integrity sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q== + +"@esbuild/netbsd-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz#65f19161432bafb3981f5f20a7ff45abb2e708e6" + integrity sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw== + +"@esbuild/netbsd-x64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz#7a3a97d77abfd11765a72f1c6f9b18f5396bcc40" + integrity sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw== + +"@esbuild/openbsd-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz#58b00238dd8f123bfff68d3acc53a6ee369af89f" + integrity sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A== + +"@esbuild/openbsd-x64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz#0ac843fda0feb85a93e288842936c21a00a8a205" + integrity sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA== + +"@esbuild/sunos-x64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz#8b7aa895e07828d36c422a4404cc2ecf27fb15c6" + integrity sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig== + +"@esbuild/win32-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz#c023afb647cabf0c3ed13f0eddfc4f1d61c66a85" + integrity sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ== + +"@esbuild/win32-ia32@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz#96c356132d2dda990098c8b8b951209c3cd743c2" + integrity sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA== + +"@esbuild/win32-x64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz#34aa0b52d0fbb1a654b596acfa595f0c7b77a77b" + integrity sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg== + "@eslint-community/eslint-utils@^4.2.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -1173,6 +1129,13 @@ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2" integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q== +"@fetch-mock/vitest@^0.2.8": + version "0.2.8" + resolved "https://registry.yarnpkg.com/@fetch-mock/vitest/-/vitest-0.2.8.tgz#b5d0be0e0eacddacdd4d4408fb3a5f5ecedbd2f7" + integrity sha512-PfnGxNSqcyWljgGf5pq62FQSF4tMm91TJklOLRPwVp7GTswejs9JEHO1gT+uthA9ZFdm9JNUAY0LOAv30G2Hsw== + dependencies: + fetch-mock "^12.3.0" + "@gerrit0/mini-shiki@^1.24.0": version "1.26.1" resolved "https://registry.yarnpkg.com/@gerrit0/mini-shiki/-/mini-shiki-1.26.1.tgz#b59884bd6013976ca66dec197492a1387fdbea52" @@ -1213,78 +1176,6 @@ wrap-ansi "^8.1.0" wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" -"@istanbuljs/load-nyc-config@^1.0.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" - integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== - dependencies: - camelcase "^5.3.1" - find-up "^4.1.0" - get-package-type "^0.1.0" - js-yaml "^3.13.1" - resolve-from "^5.0.0" - -"@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3": - version "0.1.3" - resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" - integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== - -"@jest/console@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" - integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg== - dependencies: - "@jest/types" "^29.6.3" - "@types/node" "*" - chalk "^4.0.0" - jest-message-util "^29.7.0" - jest-util "^29.7.0" - slash "^3.0.0" - -"@jest/core@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f" - integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg== - dependencies: - "@jest/console" "^29.7.0" - "@jest/reporters" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - ci-info "^3.2.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - jest-changed-files "^29.7.0" - jest-config "^29.7.0" - jest-haste-map "^29.7.0" - jest-message-util "^29.7.0" - jest-regex-util "^29.6.3" - jest-resolve "^29.7.0" - jest-resolve-dependencies "^29.7.0" - jest-runner "^29.7.0" - jest-runtime "^29.7.0" - jest-snapshot "^29.7.0" - jest-util "^29.7.0" - jest-validate "^29.7.0" - jest-watcher "^29.7.0" - micromatch "^4.0.4" - pretty-format "^29.7.0" - slash "^3.0.0" - strip-ansi "^6.0.0" - -"@jest/environment@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7" - integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw== - dependencies: - "@jest/fake-timers" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - jest-mock "^29.7.0" - "@jest/expect-utils@^28.1.3": version "28.1.3" resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-28.1.3.tgz#58561ce5db7cd253a7edddbc051fb39dda50f525" @@ -1292,73 +1183,6 @@ dependencies: jest-get-type "^28.0.2" -"@jest/expect-utils@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" - integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== - dependencies: - jest-get-type "^29.6.3" - -"@jest/expect@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2" - integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ== - dependencies: - expect "^29.7.0" - jest-snapshot "^29.7.0" - -"@jest/fake-timers@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" - integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ== - dependencies: - "@jest/types" "^29.6.3" - "@sinonjs/fake-timers" "^10.0.2" - "@types/node" "*" - jest-message-util "^29.7.0" - jest-mock "^29.7.0" - jest-util "^29.7.0" - -"@jest/globals@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d" - integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ== - dependencies: - "@jest/environment" "^29.7.0" - "@jest/expect" "^29.7.0" - "@jest/types" "^29.6.3" - jest-mock "^29.7.0" - -"@jest/reporters@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" - integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg== - dependencies: - "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" - "@jridgewell/trace-mapping" "^0.3.18" - "@types/node" "*" - chalk "^4.0.0" - collect-v8-coverage "^1.0.0" - exit "^0.1.2" - glob "^7.1.3" - graceful-fs "^4.2.9" - istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^6.0.0" - istanbul-lib-report "^3.0.0" - istanbul-lib-source-maps "^4.0.0" - istanbul-reports "^3.1.3" - jest-message-util "^29.7.0" - jest-util "^29.7.0" - jest-worker "^29.7.0" - slash "^3.0.0" - string-length "^4.0.1" - strip-ansi "^6.0.0" - v8-to-istanbul "^9.0.1" - "@jest/schemas@^28.1.3": version "28.1.3" resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-28.1.3.tgz#ad8b86a66f11f33619e3d7e1dcddd7f2d40ff905" @@ -1366,63 +1190,6 @@ dependencies: "@sinclair/typebox" "^0.24.1" -"@jest/schemas@^29.6.3": - version "29.6.3" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" - integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== - dependencies: - "@sinclair/typebox" "^0.27.8" - -"@jest/source-map@^29.6.3": - version "29.6.3" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" - integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw== - dependencies: - "@jridgewell/trace-mapping" "^0.3.18" - callsites "^3.0.0" - graceful-fs "^4.2.9" - -"@jest/test-result@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" - integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA== - dependencies: - "@jest/console" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/istanbul-lib-coverage" "^2.0.0" - collect-v8-coverage "^1.0.0" - -"@jest/test-sequencer@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce" - integrity sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw== - dependencies: - "@jest/test-result" "^29.7.0" - graceful-fs "^4.2.9" - jest-haste-map "^29.7.0" - slash "^3.0.0" - -"@jest/transform@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" - integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw== - dependencies: - "@babel/core" "^7.11.6" - "@jest/types" "^29.6.3" - "@jridgewell/trace-mapping" "^0.3.18" - babel-plugin-istanbul "^6.1.1" - chalk "^4.0.0" - convert-source-map "^2.0.0" - fast-json-stable-stringify "^2.1.0" - graceful-fs "^4.2.9" - jest-haste-map "^29.7.0" - jest-regex-util "^29.6.3" - jest-util "^29.7.0" - micromatch "^4.0.4" - pirates "^4.0.4" - slash "^3.0.0" - write-file-atomic "^4.0.2" - "@jest/types@^28.1.3": version "28.1.3" resolved "https://registry.yarnpkg.com/@jest/types/-/types-28.1.3.tgz#b05de80996ff12512bc5ceb1d208285a7d11748b" @@ -1435,18 +1202,6 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" -"@jest/types@^29.6.3": - version "29.6.3" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" - integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== - dependencies: - "@jest/schemas" "^29.6.3" - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^17.0.8" - chalk "^4.0.0" - "@jridgewell/gen-mapping@^0.3.5": version "0.3.8" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142" @@ -1466,7 +1221,7 @@ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== -"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== @@ -1479,7 +1234,7 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": version "0.3.25" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== @@ -1571,33 +1326,6 @@ resolved "https://registry.yarnpkg.com/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz#3dc35ba0f1e66b403c00b39344f870298ebb1c8e" integrity sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA== -"@peculiar/asn1-schema@^2.3.8": - version "2.3.8" - resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.8.tgz#04b38832a814e25731232dd5be883460a156da3b" - integrity sha512-ULB1XqHKx1WBU/tTFIA+uARuRoBVZ4pNdOA878RDrRbBfBGcSzi5HBkdScC6ZbHn8z7L8gmKCgPC1LHRrP46tA== - dependencies: - asn1js "^3.0.5" - pvtsutils "^1.3.5" - tslib "^2.6.2" - -"@peculiar/json-schema@^1.1.12": - version "1.1.12" - resolved "https://registry.yarnpkg.com/@peculiar/json-schema/-/json-schema-1.1.12.tgz#fe61e85259e3b5ba5ad566cb62ca75b3d3cd5339" - integrity sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w== - dependencies: - tslib "^2.0.0" - -"@peculiar/webcrypto@^1.4.5": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.5.0.tgz#9e57174c02c1291051c553600347e12b81469e10" - integrity sha512-BRs5XUAwiyCDQMsVA9IDvDa7UBR9gAvPHgugOeGng3YN6vJ9JYonyDc0lNczErgtCWtucjR5N7VtaonboD/ezg== - dependencies: - "@peculiar/asn1-schema" "^2.3.8" - "@peculiar/json-schema" "^1.1.12" - pvtsutils "^1.3.5" - tslib "^2.6.2" - webcrypto-core "^1.8.0" - "@pkgjs/parseargs@^0.11.0": version "0.11.0" resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" @@ -1608,6 +1336,106 @@ resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31" integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA== +"@polka/url@^1.0.0-next.24": + version "1.0.0-next.28" + resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.28.tgz#d45e01c4a56f143ee69c54dd6b12eade9e270a73" + integrity sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw== + +"@rollup/rollup-android-arm-eabi@4.34.3": + version "4.34.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.3.tgz#eb1b0a1d75c5f048b8d41eb30188c22292676c02" + integrity sha512-8kq/NjMKkMTGKMPldWihncOl62kgnLYk7cW+/4NCUWfS70/wz4+gQ7rMxMMpZ3dIOP/xw7wKNzIuUnN/H2GfUg== + +"@rollup/rollup-android-arm64@4.34.3": + version "4.34.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.3.tgz#850f0962a7a98a698dfc4b7530a3932b486d84c0" + integrity sha512-1PqMHiuRochQ6++SDI7SaRDWJKr/NgAlezBi5nOne6Da6IWJo3hK0TdECBDwd92IUDPG4j/bZmWuwOnomNT8wA== + +"@rollup/rollup-darwin-arm64@4.34.3": + version "4.34.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.3.tgz#150c4cfacd11ca3fe2904a25bcfd3f948aa8fd39" + integrity sha512-fqbrykX4mGV3DlCDXhF4OaMGcchd2tmLYxVt3On5oOZWVDFfdEoYAV2alzNChl8OzNaeMAGqm1f7gk7eIw/uDg== + +"@rollup/rollup-darwin-x64@4.34.3": + version "4.34.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.3.tgz#27501960a733043c2b0634c884d20cd456d1cdef" + integrity sha512-8Wxrx/KRvMsTyLTbdrMXcVKfpW51cCNW8x7iQD72xSEbjvhCY3b+w83Bea3nQfysTMR7K28esc+ZFITThXm+1w== + +"@rollup/rollup-freebsd-arm64@4.34.3": + version "4.34.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.3.tgz#a54caebb98ab71aaf67e826cc9e6a145fb30ffb5" + integrity sha512-lpBmV2qSiELh+ATQPTjQczt5hvbTLsE0c43Rx4bGxN2VpnAZWy77we7OO62LyOSZNY7CzjMoceRPc+Lt4e9J6A== + +"@rollup/rollup-freebsd-x64@4.34.3": + version "4.34.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.3.tgz#2312f47788b3e334b14edb7eee748e9d545fd856" + integrity sha512-sNPvBIXpgaYcI6mAeH13GZMXFrrw5mdZVI1M9YQPRG2LpjwL8DSxSIflZoh/B5NEuOi53kxsR/S2GKozK1vDXA== + +"@rollup/rollup-linux-arm-gnueabihf@4.34.3": + version "4.34.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.3.tgz#aaaa3f678ab3bcdf8ebda600ed2a9f04fe00d9cc" + integrity sha512-MW6N3AoC61OfE1VgnN5O1OW0gt8VTbhx9s/ZEPLBM11wEdHjeilPzOxVmmsrx5YmejpGPvez8QwGGvMU+pGxpw== + +"@rollup/rollup-linux-arm-musleabihf@4.34.3": + version "4.34.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.3.tgz#ec7c8d98c79091afda6804fdf72d1d202217b9e4" + integrity sha512-2SQkhr5xvatYq0/+H6qyW0zvrQz9LM4lxGkpWURLoQX5+yP8MsERh4uWmxFohOvwCP6l/+wgiHZ1qVwLDc7Qmw== + +"@rollup/rollup-linux-arm64-gnu@4.34.3": + version "4.34.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.3.tgz#df198a61a48db932426eee593f3699aa289e90f5" + integrity sha512-R3JLYt8YoRwKI5shJsovLpcR6pwIMui/MGG/MmxZ1DYI3iRSKI4qcYrvYgDf4Ss2oCR3RL3F3dYK7uAGQgMIuQ== + +"@rollup/rollup-linux-arm64-musl@4.34.3": + version "4.34.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.3.tgz#97b231d2ca6fdeaa8d0e02de2f1f3896bedf14a3" + integrity sha512-4XQhG8v/t3S7Rxs7rmFUuM6j09hVrTArzONS3fUZ6oBRSN/ps9IPQjVhp62P0W3KhqJdQADo/MRlYRMdgxr/3w== + +"@rollup/rollup-linux-loongarch64-gnu@4.34.3": + version "4.34.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.3.tgz#a1149b186e16d009d8fd715285e84ed63ba3cbbc" + integrity sha512-QlW1jCUZ1LHUIYCAK2FciVw1ptHsxzApYVi05q7bz2A8oNE8QxQ85NhM4arLxkAlcnS42t4avJbSfzSQwbIaKg== + +"@rollup/rollup-linux-powerpc64le-gnu@4.34.3": + version "4.34.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.3.tgz#df3c2c25f800bc0bdf5e8cfc00372b5ac761bc5b" + integrity sha512-kMbLToizVeCcN69+nnm20Dh0hrRIAjgaaL+Wh0gWZcNt8e542d2FUGtsyuNsHVNNF3gqTJrpzUGIdwMGLEUM7g== + +"@rollup/rollup-linux-riscv64-gnu@4.34.3": + version "4.34.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.3.tgz#c57b3e2c12969586f3513295cb36da96746edbf6" + integrity sha512-YgD0DnZ3CHtvXRH8rzjVSxwI0kMTr0RQt3o1N92RwxGdx7YejzbBO0ELlSU48DP96u1gYYVWfUhDRyaGNqJqJg== + +"@rollup/rollup-linux-s390x-gnu@4.34.3": + version "4.34.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.3.tgz#e6ac0788471a9f7400b358eb5f91292efcc900c4" + integrity sha512-dIOoOz8altjp6UjAi3U9EW99s8nta4gzi52FeI45GlPyrUH4QixUoBMH9VsVjt+9A2RiZBWyjYNHlJ/HmJOBCQ== + +"@rollup/rollup-linux-x64-gnu@4.34.3": + version "4.34.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.3.tgz#017bb2808665d69ba55740cae02708ea8cb45885" + integrity sha512-lOyG3aF4FTKrhpzXfMmBXgeKUUXdAWmP2zSNf8HTAXPqZay6QYT26l64hVizBjq+hJx3pl0DTEyvPi9sTA6VGA== + +"@rollup/rollup-linux-x64-musl@4.34.3": + version "4.34.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.3.tgz#ac3de953f8e31b08f1528e17f0524af15b2df38c" + integrity sha512-usztyYLu2i+mYzzOjqHZTaRXbUOqw3P6laNUh1zcqxbPH1P2Tz/QdJJCQSnGxCtsRQeuU2bCyraGMtMumC46rw== + +"@rollup/rollup-win32-arm64-msvc@4.34.3": + version "4.34.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.3.tgz#183fb4b849accdf68d430894ada2b88eea95a140" + integrity sha512-ojFOKaz/ZyalIrizdBq2vyc2f0kFbJahEznfZlxdB6pF9Do6++i1zS5Gy6QLf8D7/S57MHrmBLur6AeRYeQXSA== + +"@rollup/rollup-win32-ia32-msvc@4.34.3": + version "4.34.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.3.tgz#3fd1b93867442ecd3d2329b902b111853600cc6c" + integrity sha512-K/V97GMbNa+Da9mGcZqmSl+DlJmWfHXTuI9V8oB2evGsQUtszCl67+OxWjBKpeOnYwox9Jpmt/J6VhpeRCYqow== + +"@rollup/rollup-win32-x64-msvc@4.34.3": + version "4.34.3" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.3.tgz#2cd47d213ddd921bab1470a3e31312ee37aac08a" + integrity sha512-CUypcYP31Q8O04myV6NKGzk9GVXslO5EJNfmARNSzLF2A+5rmZUlDJ4et6eoJaZgBT9wrC2p4JZH04Vkic8HdQ== + "@rtsao/scc@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" @@ -1639,25 +1467,6 @@ resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.51.tgz#645f33fe4e02defe26f2f5c0410e1c094eac7f5f" integrity sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA== -"@sinclair/typebox@^0.27.8": - version "0.27.8" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" - integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== - -"@sinonjs/commons@^3.0.0": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" - integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== - dependencies: - type-detect "4.0.8" - -"@sinonjs/fake-timers@^10.0.2": - version "10.3.0" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" - integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== - dependencies: - "@sinonjs/commons" "^3.0.0" - "@snyk/github-codeowners@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@snyk/github-codeowners/-/github-codeowners-1.1.0.tgz#45b99732c3c38b5f5b47e43d2b0c9db67a6d2bcc" @@ -1678,11 +1487,6 @@ estraverse "^5.3.0" picomatch "^4.0.2" -"@tootallnate/once@2": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" - integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== - "@tsconfig/node10@^1.0.7": version "1.0.11" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" @@ -1703,39 +1507,6 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== -"@types/babel__core@^7.1.14": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" - integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== - dependencies: - "@babel/parser" "^7.20.7" - "@babel/types" "^7.20.7" - "@types/babel__generator" "*" - "@types/babel__template" "*" - "@types/babel__traverse" "*" - -"@types/babel__generator@*": - version "7.6.8" - resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.8.tgz#f836c61f48b1346e7d2b0d93c6dacc5b9535d3ab" - integrity sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw== - dependencies: - "@babel/types" "^7.0.0" - -"@types/babel__template@*": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" - integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== - dependencies: - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" - -"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": - version "7.20.6" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.6.tgz#8dc9f0ae0f202c08d8d4dab648912c8d6038e3f7" - integrity sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg== - dependencies: - "@babel/types" "^7.20.7" - "@types/bs58@^4.0.1": version "4.0.4" resolved "https://registry.yarnpkg.com/@types/bs58/-/bs58-4.0.4.tgz#49fbcb0c7db5f7cea26f0e0f61dc4a41a2445aab" @@ -1756,6 +1527,11 @@ dependencies: "@types/ms" "*" +"@types/estree@1.0.6", "@types/estree@^1.0.0": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" + integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== + "@types/events@^3.0.0": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.3.tgz#a8ef894305af28d1fc6d2dfdfc98e899591ea529" @@ -1766,13 +1542,6 @@ resolved "https://registry.yarnpkg.com/@types/glob-to-regexp/-/glob-to-regexp-0.4.4.tgz#409e71290253203185b1ea8a3d6ea406a4bdc902" integrity sha512-nDKoaKJYbnn1MZxUY0cA1bPmmgZbg0cTq7Rh13d0KWYNOiKbqoR+2d89SnRPszGh7ROzSwZ/GOjZ4jPbmmZ6Eg== -"@types/graceful-fs@^4.1.3": - version "4.1.9" - resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4" - integrity sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ== - dependencies: - "@types/node" "*" - "@types/hast@^3.0.4": version "3.0.4" resolved "https://registry.yarnpkg.com/@types/hast/-/hast-3.0.4.tgz#1d6b39993b82cea6ad783945b0508c25903e15aa" @@ -1780,7 +1549,7 @@ dependencies: "@types/unist" "*" -"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": version "2.0.6" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== @@ -1799,23 +1568,6 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@^29.0.0": - version "29.5.12" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.12.tgz#7f7dc6eb4cf246d2474ed78744b05d06ce025544" - integrity sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw== - dependencies: - expect "^29.0.0" - pretty-format "^29.0.0" - -"@types/jsdom@^20.0.0": - version "20.0.1" - resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-20.0.1.tgz#07c14bc19bd2f918c1929541cdaacae894744808" - integrity sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ== - dependencies: - "@types/node" "*" - "@types/tough-cookie" "*" - parse5 "^7.0.0" - "@types/json5@^0.0.29": version "0.0.29" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" @@ -1860,11 +1612,6 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== -"@types/tough-cookie@*": - version "4.0.5" - resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" - integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA== - "@types/unist@*": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.3.tgz#acaab0f919ce69cce629c2d4ed2eb4adc1b6c20c" @@ -1913,6 +1660,14 @@ "@typescript-eslint/visitor-keys" "8.23.0" debug "^4.3.4" +"@typescript-eslint/scope-manager@7.18.0": + version "7.18.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz#c928e7a9fc2c0b3ed92ab3112c614d6bd9951c83" + integrity sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA== + dependencies: + "@typescript-eslint/types" "7.18.0" + "@typescript-eslint/visitor-keys" "7.18.0" + "@typescript-eslint/scope-manager@8.21.0": version "8.21.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.21.0.tgz#d08d94e2a34b4ccdcc975543c25bb62917437500" @@ -1939,6 +1694,11 @@ debug "^4.3.4" ts-api-utils "^2.0.1" +"@typescript-eslint/types@7.18.0": + version "7.18.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.18.0.tgz#b90a57ccdea71797ffffa0321e744f379ec838c9" + integrity sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ== + "@typescript-eslint/types@8.21.0": version "8.21.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.21.0.tgz#58f30aec8db8212fd886835dc5969cdf47cb29f5" @@ -1949,6 +1709,20 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.23.0.tgz#3355f6bcc5ebab77ef6dcbbd1113ec0a683a234a" integrity sha512-1sK4ILJbCmZOTt9k4vkoulT6/y5CHJ1qUYxqpF1K/DBAd8+ZUL4LlSCxOssuH5m4rUaaN0uS0HlVPvd45zjduQ== +"@typescript-eslint/typescript-estree@7.18.0": + version "7.18.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz#b5868d486c51ce8f312309ba79bdb9f331b37931" + integrity sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA== + dependencies: + "@typescript-eslint/types" "7.18.0" + "@typescript-eslint/visitor-keys" "7.18.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + minimatch "^9.0.4" + semver "^7.6.0" + ts-api-utils "^1.3.0" + "@typescript-eslint/typescript-estree@8.21.0": version "8.21.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.21.0.tgz#5ce71acdbed3b97b959f6168afba5a03c88f69a9" @@ -1987,7 +1761,17 @@ "@typescript-eslint/types" "8.23.0" "@typescript-eslint/typescript-estree" "8.23.0" -"@typescript-eslint/utils@^6.0.0 || ^7.0.0 || ^8.0.0", "@typescript-eslint/utils@^8.13.0": +"@typescript-eslint/utils@^7.7.1": + version "7.18.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-7.18.0.tgz#bca01cde77f95fc6a8d5b0dbcbfb3d6ca4be451f" + integrity sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@typescript-eslint/scope-manager" "7.18.0" + "@typescript-eslint/types" "7.18.0" + "@typescript-eslint/typescript-estree" "7.18.0" + +"@typescript-eslint/utils@^8.13.0": version "8.21.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.21.0.tgz#bc4874fbc30feb3298b926e3b03d94570b3999c5" integrity sha512-xcXBfcq0Kaxgj7dwejMbFyq7IOHgpNMtVuDveK7w3ZGwG9owKzhALVwKpTF2yrZmEwl9SWdetf3fxNzJQaVuxw== @@ -1997,6 +1781,14 @@ "@typescript-eslint/types" "8.21.0" "@typescript-eslint/typescript-estree" "8.21.0" +"@typescript-eslint/visitor-keys@7.18.0": + version "7.18.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz#0564629b6124d67607378d0f0332a0495b25e7d7" + integrity sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg== + dependencies: + "@typescript-eslint/types" "7.18.0" + eslint-visitor-keys "^3.4.3" + "@typescript-eslint/visitor-keys@8.21.0": version "8.21.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.21.0.tgz#a89744c4cdc83b5c761eb5878befe6c33d1481b2" @@ -2018,32 +1810,91 @@ resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== -abab@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" - integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== +"@vitest/expect@3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-3.0.5.tgz#aa0acd0976cf56842806e5dcaebd446543966b14" + integrity sha512-nNIOqupgZ4v5jWuQx2DSlHLEs7Q4Oh/7AYwNyE+k0UQzG7tSmjPXShUikn1mpNGzYEN2jJbTvLejwShMitovBA== + dependencies: + "@vitest/spy" "3.0.5" + "@vitest/utils" "3.0.5" + chai "^5.1.2" + tinyrainbow "^2.0.0" -acorn-globals@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-7.0.1.tgz#0dbf05c44fa7c94332914c02066d5beff62c40c3" - integrity sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q== +"@vitest/mocker@3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-3.0.5.tgz#8dce3dc4cb0adfd9d554531cea836244f8c36bcd" + integrity sha512-CLPNBFBIE7x6aEGbIjaQAX03ZZlBMaWwAjBdMkIf/cAn6xzLTiM3zYqO/WAbieEjsAZir6tO71mzeHZoodThvw== + dependencies: + "@vitest/spy" "3.0.5" + estree-walker "^3.0.3" + magic-string "^0.30.17" + +"@vitest/pretty-format@3.0.5", "@vitest/pretty-format@^3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-3.0.5.tgz#10ae6a83ccc1a866e31b2d0c1a7a977ade02eff9" + integrity sha512-CjUtdmpOcm4RVtB+up8r2vVDLR16Mgm/bYdkGFe3Yj/scRfCpbSi2W/BDSDcFK7ohw8UXvjMbOp9H4fByd/cOA== + dependencies: + tinyrainbow "^2.0.0" + +"@vitest/runner@3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-3.0.5.tgz#c5960a1169465a2b9ac21f1d24a4cf1fe67c7501" + integrity sha512-BAiZFityFexZQi2yN4OX3OkJC6scwRo8EhRB0Z5HIGGgd2q+Nq29LgHU/+ovCtd0fOfXj5ZI6pwdlUmC5bpi8A== + dependencies: + "@vitest/utils" "3.0.5" + pathe "^2.0.2" + +"@vitest/snapshot@3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-3.0.5.tgz#afd0ae472dc5893b0bb10e3e673ef649958663f4" + integrity sha512-GJPZYcd7v8QNUJ7vRvLDmRwl+a1fGg4T/54lZXe+UOGy47F9yUfE18hRCtXL5aHN/AONu29NGzIXSVFh9K0feA== dependencies: - acorn "^8.1.0" - acorn-walk "^8.0.2" + "@vitest/pretty-format" "3.0.5" + magic-string "^0.30.17" + pathe "^2.0.2" + +"@vitest/spy@3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-3.0.5.tgz#7bb5d84ec21cc0d62170fda4e31cd0b46c1aeb8b" + integrity sha512-5fOzHj0WbUNqPK6blI/8VzZdkBlQLnT25knX0r4dbZI9qoZDf3qAdjoMmDcLG5A83W6oUUFJgUd0EYBc2P5xqg== + dependencies: + tinyspy "^3.0.2" + +"@vitest/ui@^3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@vitest/ui/-/ui-3.0.5.tgz#4c57cb91d84a53afff07f6282ebb355d0de25acd" + integrity sha512-gw2noso6WI+2PeMVCZFntdATS6xl9qhQcbhkPQ9sOmx/Xn0f4Bx4KDSbD90jpJPF0l5wOzSoGCmKyVR3W612mg== + dependencies: + "@vitest/utils" "3.0.5" + fflate "^0.8.2" + flatted "^3.3.2" + pathe "^2.0.2" + sirv "^3.0.0" + tinyglobby "^0.2.10" + tinyrainbow "^2.0.0" + +"@vitest/utils@3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-3.0.5.tgz#dc3eaefd3534598917e939af59d9a9b6a5be5082" + integrity sha512-N9AX0NUoUtVwKwy21JtwzaqR5L5R5A99GAbrHfCCXK1lp593i/3AZAXhSP43wRQuxYsflrdzEfXZFo1reR1Nkg== + dependencies: + "@vitest/pretty-format" "3.0.5" + loupe "^3.1.2" + tinyrainbow "^2.0.0" acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn-walk@^8.0.2, acorn-walk@^8.1.1: +acorn-walk@^8.1.1: version "8.3.3" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.3.tgz#9caeac29eefaa0c41e3d4c65137de4d6f34df43e" integrity sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw== dependencies: acorn "^8.11.0" -acorn@^8.1.0, acorn@^8.11.0, acorn@^8.4.1, acorn@^8.8.1, acorn@^8.9.0: +acorn@^8.11.0, acorn@^8.4.1, acorn@^8.9.0: version "8.12.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== @@ -2053,12 +1904,10 @@ acorn@^8.14.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== -agent-base@6: - version "6.0.2" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" - integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== - dependencies: - debug "4" +agent-base@^7.1.0, agent-base@^7.1.2: + version "7.1.3" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.3.tgz#29435eb821bc4194633a5b89e5bc4703bafc25a1" + integrity sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw== aggregate-error@^3.0.0: version "3.1.0" @@ -2093,13 +1942,6 @@ another-json@^0.2.0: resolved "https://registry.yarnpkg.com/another-json/-/another-json-0.2.0.tgz#b5f4019c973b6dd5c6506a2d93469cb6d32aeedc" integrity sha512-/Ndrl68UQLhnCdsAzEXLMFuOR546o2qbYRqCglaNHbjXrwG1ayTcdwr3zkSGOGtGXDyR5X9nCFfnyG2AFJIsqg== -ansi-escapes@^4.2.1: - version "4.3.2" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" - integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== - dependencies: - type-fest "^0.21.3" - ansi-escapes@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-7.0.0.tgz#00fc19f491bbb18e1d481b97868204f92109bfe7" @@ -2141,7 +1983,7 @@ ansi-styles@^6.0.0, ansi-styles@^6.1.0, ansi-styles@^6.2.1: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== -anymatch@^3.0.3, anymatch@~3.1.2: +anymatch@~3.1.2: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== @@ -2159,13 +2001,6 @@ arg@^4.1.0: resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" - argparse@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" @@ -2191,6 +2026,11 @@ array-includes@^3.1.8: get-intrinsic "^1.2.4" is-string "^1.0.7" +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + array.prototype.findlastindex@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz#8c35a755c72908719453f87145ca011e39334d0d" @@ -2237,14 +2077,10 @@ arraybuffer.prototype.slice@^1.0.3: is-array-buffer "^3.0.4" is-shared-array-buffer "^1.0.2" -asn1js@^3.0.1, asn1js@^3.0.5: - version "3.0.5" - resolved "https://registry.yarnpkg.com/asn1js/-/asn1js-3.0.5.tgz#5ea36820443dbefb51cc7f88a2ebb5b462114f38" - integrity sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ== - dependencies: - pvtsutils "^1.3.2" - pvutils "^1.1.3" - tslib "^2.4.0" +assertion-error@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7" + integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA== asynckit@^0.4.0: version "0.4.0" @@ -2258,44 +2094,10 @@ available-typed-arrays@^1.0.7: dependencies: possible-typed-array-names "^1.0.0" -babel-jest@^29.0.0, babel-jest@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" - integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg== - dependencies: - "@jest/transform" "^29.7.0" - "@types/babel__core" "^7.1.14" - babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^29.6.3" - chalk "^4.0.0" - graceful-fs "^4.2.9" - slash "^3.0.0" - -babel-plugin-istanbul@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" - integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@istanbuljs/load-nyc-config" "^1.0.0" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-instrument "^5.0.4" - test-exclude "^6.0.0" - -babel-plugin-jest-hoist@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" - integrity sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg== - dependencies: - "@babel/template" "^7.3.3" - "@babel/types" "^7.3.3" - "@types/babel__core" "^7.1.14" - "@types/babel__traverse" "^7.0.6" - -babel-plugin-polyfill-corejs2@^0.4.10: - version "0.4.12" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.12.tgz#ca55bbec8ab0edeeef3d7b8ffd75322e210879a9" - integrity sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og== +babel-plugin-polyfill-corejs2@^0.4.10: + version "0.4.12" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.12.tgz#ca55bbec8ab0edeeef3d7b8ffd75322e210879a9" + integrity sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og== dependencies: "@babel/compat-data" "^7.22.6" "@babel/helper-define-polyfill-provider" "^0.6.3" @@ -2321,32 +2123,6 @@ babel-plugin-search-and-replace@^1.1.1: resolved "https://registry.yarnpkg.com/babel-plugin-search-and-replace/-/babel-plugin-search-and-replace-1.1.1.tgz#2e5b4488e41d9eba1c220651b1a9b350fdf10915" integrity sha512-fjP2VTF3mxxOUnc96mdK22llH92A6gu7A5AFapJmgnqsQi3bqLduIRP0FpA2r5vRZOYPpnX4rE5izQlpsMBjSA== -babel-preset-current-node-syntax@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" - integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== - dependencies: - "@babel/plugin-syntax-async-generators" "^7.8.4" - "@babel/plugin-syntax-bigint" "^7.8.3" - "@babel/plugin-syntax-class-properties" "^7.8.3" - "@babel/plugin-syntax-import-meta" "^7.8.3" - "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.8.3" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-syntax-top-level-await" "^7.8.3" - -babel-preset-jest@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" - integrity sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA== - dependencies: - babel-plugin-jest-hoist "^29.6.3" - babel-preset-current-node-syntax "^1.0.0" - balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -2408,23 +2184,16 @@ bs58@^6.0.0: dependencies: base-x "^5.0.0" -bser@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" - integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== - dependencies: - node-int64 "^0.4.0" - -buffer-from@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" - integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== - builtin-modules@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== +cac@^6.7.14: + version "6.7.14" + resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" + integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== + call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" @@ -2441,21 +2210,22 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -camelcase@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== - -camelcase@^6.2.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" - integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== - caniuse-lite@^1.0.30001688: version "1.0.30001697" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001697.tgz#040bbbb54463c4b4b3377c716b34a322d16e6fc7" integrity sha512-GwNPlWJin8E+d7Gxq96jxM6w0w+VFeyyXRsjU58emtkYqnbwHqXm5uT2uCmO0RQE9htWknOP4xtBlLmM/gWxvQ== +chai@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/chai/-/chai-5.1.2.tgz#3afbc340b994ae3610ca519a6c70ace77ad4378d" + integrity sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw== + dependencies: + assertion-error "^2.0.1" + check-error "^2.1.1" + deep-eql "^5.0.1" + loupe "^3.1.0" + pathval "^2.0.0" + chalk@5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.2.0.tgz#249623b7d66869c673699fb66d65723e54dfcfb3" @@ -2483,10 +2253,10 @@ chalk@^5.4.1: resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.4.1.tgz#1b48bf0963ec158dce2aacf69c093ae2dd2092d8" integrity sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w== -char-regex@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" - integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== +check-error@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-2.1.1.tgz#87eb876ae71ee388fa0471fe423f494be1d96ccc" + integrity sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw== chokidar@^3.6.0: version "3.6.0" @@ -2513,11 +2283,6 @@ ci-info@^4.0.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.1.0.tgz#92319d2fa29d2620180ea5afed31f589bc98cf83" integrity sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A== -cjs-module-lexer@^1.0.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz#c485341ae8fd999ca4ee5af2d7a1c9ae01e0099c" - integrity sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q== - clean-regexp@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/clean-regexp/-/clean-regexp-1.0.0.tgz#8df7c7aae51fd36874e8f8d05b9180bc11a3fed7" @@ -2545,30 +2310,11 @@ cli-truncate@^4.0.0: slice-ansi "^5.0.0" string-width "^7.0.0" -cliui@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" - integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.1" - wrap-ansi "^7.0.0" - clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== - -collect-v8-coverage@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9" - integrity sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q== - color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -2647,24 +2393,6 @@ core-js-compat@^3.38.0, core-js-compat@^3.38.1: dependencies: browserslist "^4.24.3" -core-js@^3.0.0: - version "3.37.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.37.1.tgz#d21751ddb756518ac5a00e4d66499df981a62db9" - integrity sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw== - -create-jest@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320" - integrity sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q== - dependencies: - "@jest/types" "^29.6.3" - chalk "^4.0.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - jest-config "^29.7.0" - jest-util "^29.7.0" - prompts "^2.0.1" - create-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" @@ -2679,31 +2407,21 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -cssom@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36" - integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw== - -cssom@~0.3.6: - version "0.3.8" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" - integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== - -cssstyle@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" - integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== +cssstyle@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-4.2.1.tgz#5142782410fea95db66fb68147714a652a7c2381" + integrity sha512-9+vem03dMXG7gDmZ62uqmRiMRNtinIZ9ZyuF6BdxzfOD+FdN5hretzynkn0ReS2DO2GSw76RWHs0UmJPI2zUjw== dependencies: - cssom "~0.3.6" + "@asamuzakjp/css-color" "^2.8.2" + rrweb-cssom "^0.8.0" -data-urls@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.2.tgz#9cf24a477ae22bcef5cd5f6f0bfbc1d2d3be9143" - integrity sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ== +data-urls@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-5.0.0.tgz#2f76906bce1824429ffecb6920f45a0b30f00dde" + integrity sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg== dependencies: - abab "^2.0.6" - whatwg-mimetype "^3.0.0" - whatwg-url "^11.0.0" + whatwg-mimetype "^4.0.0" + whatwg-url "^14.0.0" data-view-buffer@^1.0.1: version "1.0.1" @@ -2732,10 +2450,10 @@ data-view-byte-offset@^1.0.0: es-errors "^1.3.0" is-data-view "^1.0.1" -debug@4, debug@^4.3.2: - version "4.3.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" - integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.4, debug@^4.3.6, debug@^4.3.7, debug@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" + integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== dependencies: ms "^2.1.3" @@ -2746,33 +2464,28 @@ debug@^3.2.7: dependencies: ms "^2.1.1" -debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.4, debug@^4.3.6, debug@^4.3.7, debug@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" - integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== +debug@^4.3.2: + version "4.3.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" + integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== dependencies: ms "^2.1.3" -decimal.js@^10.4.2: - version "10.4.3" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" - integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== +decimal.js@^10.4.3: + version "10.5.0" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.5.0.tgz#0f371c7cf6c4898ce0afb09836db73cd82010f22" + integrity sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw== -dedent@^1.0.0: - version "1.5.3" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.3.tgz#99aee19eb9bae55a67327717b6e848d0bf777e5a" - integrity sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ== +deep-eql@^5.0.1: + version "5.0.2" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341" + integrity sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q== deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== -deepmerge@^4.2.2: - version "4.3.1" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" - integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== - defaults@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a" @@ -2808,26 +2521,23 @@ dequal@^2.0.3: resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== -detect-newline@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" - integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== - diff-sequences@^28.1.1: version "28.1.1" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-28.1.1.tgz#9989dc731266dc2903457a70e996f3a041913ac6" integrity sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw== -diff-sequences@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" - integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== - diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" @@ -2842,13 +2552,6 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -domexception@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673" - integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw== - dependencies: - webidl-conversions "^7.0.0" - eastasianwidth@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" @@ -2868,11 +2571,6 @@ electron-to-chromium@^1.5.73: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.91.tgz#cf5567f6853062493242133aefd4dc8dc8440abd" integrity sha512-sNSHHyq048PFmZY4S90ax61q+gLCs0X0YmcOII9wG9S2XwbVr+h4VW2wWhnbp/Eys3cCwTxVF292W3qPaxIapQ== -emittery@^0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" - integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== - emoji-regex@^10.3.0: version "10.4.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.4.0.tgz#03553afea80b3975749cfcb36f776ca268e413d4" @@ -2904,7 +2602,7 @@ enhanced-resolve@^5.18.0: graceful-fs "^4.2.4" tapable "^2.2.0" -entities@^4.4.0: +entities@^4.4.0, entities@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== @@ -2985,7 +2683,7 @@ es-errors@^1.2.1, es-errors@^1.3.0: resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== -es-module-lexer@^1.5.3: +es-module-lexer@^1.5.3, es-module-lexer@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.6.0.tgz#da49f587fd9e68ee2404fe4e256c0c7d3a81be21" integrity sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ== @@ -3022,10 +2720,36 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -escalade@^3.1.1: - version "3.1.2" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" - integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== +esbuild@^0.24.2: + version "0.24.2" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.24.2.tgz#b5b55bee7de017bff5fb8a4e3e44f2ebe2c3567d" + integrity sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA== + optionalDependencies: + "@esbuild/aix-ppc64" "0.24.2" + "@esbuild/android-arm" "0.24.2" + "@esbuild/android-arm64" "0.24.2" + "@esbuild/android-x64" "0.24.2" + "@esbuild/darwin-arm64" "0.24.2" + "@esbuild/darwin-x64" "0.24.2" + "@esbuild/freebsd-arm64" "0.24.2" + "@esbuild/freebsd-x64" "0.24.2" + "@esbuild/linux-arm" "0.24.2" + "@esbuild/linux-arm64" "0.24.2" + "@esbuild/linux-ia32" "0.24.2" + "@esbuild/linux-loong64" "0.24.2" + "@esbuild/linux-mips64el" "0.24.2" + "@esbuild/linux-ppc64" "0.24.2" + "@esbuild/linux-riscv64" "0.24.2" + "@esbuild/linux-s390x" "0.24.2" + "@esbuild/linux-x64" "0.24.2" + "@esbuild/netbsd-arm64" "0.24.2" + "@esbuild/netbsd-x64" "0.24.2" + "@esbuild/openbsd-arm64" "0.24.2" + "@esbuild/openbsd-x64" "0.24.2" + "@esbuild/sunos-x64" "0.24.2" + "@esbuild/win32-arm64" "0.24.2" + "@esbuild/win32-ia32" "0.24.2" + "@esbuild/win32-x64" "0.24.2" escalade@^3.2.0: version "3.2.0" @@ -3047,17 +2771,6 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -escodegen@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17" - integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w== - dependencies: - esprima "^4.0.1" - estraverse "^5.2.0" - esutils "^2.0.2" - optionalDependencies: - source-map "~0.6.1" - eslint-config-google@^0.14.0: version "0.14.0" resolved "https://registry.yarnpkg.com/eslint-config-google/-/eslint-config-google-0.14.0.tgz#4f5f8759ba6e11b424294a219dbfa18c508bcc1a" @@ -3131,13 +2844,6 @@ eslint-plugin-import@^2.26.0: string.prototype.trimend "^1.0.8" tsconfig-paths "^3.15.0" -eslint-plugin-jest@^28.0.0: - version "28.11.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-28.11.0.tgz#2641ecb4411941bbddb3d7cf8a8ff1163fbb510e" - integrity sha512-QAfipLcNCWLVocVbZW8GimKn5p5iiMcgGbRzz8z/P5q7xw+cNEpYqyzFMtIF/ZgF2HLOyy+dYBut+DoYolvqig== - dependencies: - "@typescript-eslint/utils" "^6.0.0 || ^7.0.0 || ^8.0.0" - eslint-plugin-jsdoc@^50.0.0: version "50.6.3" resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-50.6.3.tgz#668dc4d32e823c84ede7310cffbf70c9d370d291" @@ -3203,6 +2909,13 @@ eslint-plugin-unicorn@^56.0.0: semver "^7.6.3" strip-indent "^3.0.0" +eslint-plugin-vitest@^0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/eslint-plugin-vitest/-/eslint-plugin-vitest-0.5.4.tgz#2838a40ee116ba7c15eb6132df31371d960e3bf5" + integrity sha512-um+odCkccAHU53WdKAw39MY61+1x990uXjSPguUCq3VcEHdqJrOb8OTMrbYlY6f9jAKx7x98kLVlIe3RJeJqoQ== + dependencies: + "@typescript-eslint/utils" "^7.7.1" + eslint-rule-composer@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz#79320c927b0c5c0d3d3d2b76c8b4a488f25bbaf9" @@ -3320,11 +3033,6 @@ espree@^9.6.0, espree@^9.6.1: acorn-jsx "^5.3.2" eslint-visitor-keys "^3.4.1" -esprima@^4.0.0, esprima@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - esquery@^1.4.2, esquery@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" @@ -3349,6 +3057,13 @@ estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== +estree-walker@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d" + integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g== + dependencies: + "@types/estree" "^1.0.0" + esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -3364,21 +3079,6 @@ events@^3.2.0: resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== -execa@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" - integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== - dependencies: - cross-spawn "^7.0.3" - get-stream "^6.0.0" - human-signals "^2.1.0" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.1" - onetime "^5.1.2" - signal-exit "^3.0.3" - strip-final-newline "^2.0.0" - execa@^8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/execa/-/execa-8.0.1.tgz#51f6a5943b580f963c3ca9c6321796db8cc39b8c" @@ -3394,10 +3094,10 @@ execa@^8.0.1: signal-exit "^4.1.0" strip-final-newline "^3.0.0" -exit@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" - integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== +expect-type@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.1.0.tgz#a146e414250d13dfc49eafcfd1344a4060fa4c75" + integrity sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA== expect@^28.1.0: version "28.1.3" @@ -3410,17 +3110,6 @@ expect@^28.1.0: jest-message-util "^28.1.3" jest-util "^28.1.3" -expect@^29.0.0, expect@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" - integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== - dependencies: - "@jest/expect-utils" "^29.7.0" - jest-get-type "^29.6.3" - jest-matcher-utils "^29.7.0" - jest-message-util "^29.7.0" - jest-util "^29.7.0" - fake-indexeddb@^5.0.2: version "5.0.2" resolved "https://registry.yarnpkg.com/fake-indexeddb/-/fake-indexeddb-5.0.2.tgz#8e0b6c75c6dc6639cbb50c1aa948772147d7c93e" @@ -3431,7 +3120,7 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-glob@^3.3.2, fast-glob@^3.3.3: +fast-glob@^3.2.9, fast-glob@^3.3.2, fast-glob@^3.3.3: version "3.3.3" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== @@ -3442,7 +3131,7 @@ fast-glob@^3.3.2, fast-glob@^3.3.3: merge2 "^1.3.0" micromatch "^4.0.8" -fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: +fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== @@ -3459,46 +3148,26 @@ fastq@^1.15.0, fastq@^1.6.0: dependencies: reusify "^1.0.4" -fb-watchman@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" - integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== - dependencies: - bser "2.1.1" +fdir@^6.4.2: + version "6.4.3" + resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.4.3.tgz#011cdacf837eca9b811c89dbb902df714273db72" + integrity sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw== -fetch-mock-jest@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/fetch-mock-jest/-/fetch-mock-jest-1.5.1.tgz#0e13df990d286d9239e284f12b279ed509bf53cd" - integrity sha512-+utwzP8C+Pax1GSka3nFXILWMY3Er2L+s090FOgqVNrNCPp0fDqgXnAHAJf12PLHi0z4PhcTaZNTz8e7K3fjqQ== - dependencies: - fetch-mock "^9.11.0" - -fetch-mock@11.1.5: - version "11.1.5" - resolved "https://registry.yarnpkg.com/fetch-mock/-/fetch-mock-11.1.5.tgz#77f78942f3733cfba47fc232b8528d1138a6761f" - integrity sha512-KHmZDnZ1ry0pCTrX4YG5DtThHi0MH+GNI9caESnzX/nMJBrvppUHMvLx47M0WY9oAtKOMiPfZDRpxhlHg89BOA== +fetch-mock@^12.3.0: + version "12.3.0" + resolved "https://registry.yarnpkg.com/fetch-mock/-/fetch-mock-12.3.0.tgz#3b584ed53f564260bef27773cdc1aa87130ce8e8" + integrity sha512-+ZHzLuzrKpP3u5PZo8ghFP1Kr3UJUTZ5PT/uQZtLv7UagDCVRt1bSzVg6MoTFdjQ0GXsx/crq2t0tGabkbH2yA== dependencies: "@types/glob-to-regexp" "^0.4.4" dequal "^2.0.3" glob-to-regexp "^0.4.1" - is-subset "^0.1.1" + is-subset-of "^3.1.10" regexparam "^3.0.0" -fetch-mock@^9.11.0: - version "9.11.0" - resolved "https://registry.yarnpkg.com/fetch-mock/-/fetch-mock-9.11.0.tgz#371c6fb7d45584d2ae4a18ee6824e7ad4b637a3f" - integrity sha512-PG1XUv+x7iag5p/iNHD4/jdpxL9FtVSqRMUQhPab4hVDt80T1MH5ehzVrL2IdXO9Q2iBggArFvPqjUbHFuI58Q== - dependencies: - "@babel/core" "^7.0.0" - "@babel/runtime" "^7.0.0" - core-js "^3.0.0" - debug "^4.1.1" - glob-to-regexp "^0.4.0" - is-subset "^0.1.1" - lodash.isequal "^4.5.0" - path-to-regexp "^2.2.1" - querystring "^0.2.0" - whatwg-url "^6.5.0" +fflate@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea" + integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A== file-entry-cache@^6.0.1: version "6.0.1" @@ -3514,7 +3183,7 @@ fill-range@^7.1.1: dependencies: to-regex-range "^5.0.1" -find-up@^4.0.0, find-up@^4.1.0: +find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== @@ -3544,6 +3213,11 @@ flatted@^3.2.9: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== +flatted@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.2.tgz#adba1448a9841bec72b42c532ea23dbbedef1a27" + integrity sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA== + for-each@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -3559,10 +3233,10 @@ foreground-child@^3.1.0: cross-spawn "^7.0.0" signal-exit "^4.0.1" -form-data@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" - integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== +form-data@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.1.tgz#ba1076daaaa5bfd7e99c1a6cb02aa0a5cff90d48" + integrity sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw== dependencies: asynckit "^0.4.0" combined-stream "^1.0.8" @@ -3578,7 +3252,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@^2.3.2, fsevents@~2.3.2: +fsevents@~2.3.2, fsevents@~2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== @@ -3608,11 +3282,6 @@ gensync@^1.0.0-beta.2: resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== -get-caller-file@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - get-east-asian-width@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz#21b4071ee58ed04ee0db653371b55b4299875389" @@ -3629,16 +3298,6 @@ get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@ has-symbols "^1.0.3" hasown "^2.0.0" -get-package-type@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" - integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== - -get-stream@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" - integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== - get-stream@^8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2" @@ -3674,7 +3333,7 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" -glob-to-regexp@^0.4.0, glob-to-regexp@^0.4.1: +glob-to-regexp@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== @@ -3691,7 +3350,7 @@ glob@^11.0.0: package-json-from-dist "^1.0.0" path-scurry "^2.0.0" -glob@^7.1.3, glob@^7.1.4, glob@^7.2.0: +glob@^7.1.3, glob@^7.2.0: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -3728,6 +3387,18 @@ globalthis@^1.0.3: define-properties "^1.2.1" gopd "^1.0.1" +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + gopd@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" @@ -3796,40 +3467,29 @@ hosted-git-info@^2.1.4: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== -html-encoding-sniffer@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9" - integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA== +html-encoding-sniffer@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz#696df529a7cfd82446369dc5193e590a3735b448" + integrity sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ== dependencies: - whatwg-encoding "^2.0.0" - -html-escaper@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" - integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + whatwg-encoding "^3.1.1" -http-proxy-agent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" - integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== +http-proxy-agent@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" + integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== dependencies: - "@tootallnate/once" "2" - agent-base "6" - debug "4" + agent-base "^7.1.0" + debug "^4.3.4" -https-proxy-agent@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" - integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== +https-proxy-agent@^7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9" + integrity sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw== dependencies: - agent-base "6" + agent-base "^7.1.2" debug "4" -human-signals@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" - integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== - human-signals@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28" @@ -3860,14 +3520,6 @@ import-fresh@^3.2.1: parent-module "^1.0.0" resolve-from "^4.0.0" -import-local@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" - integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== - dependencies: - pkg-dir "^4.2.0" - resolve-cwd "^3.0.0" - imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" @@ -4004,11 +3656,6 @@ is-fullwidth-code-point@^5.0.0: dependencies: get-east-asian-width "^1.0.0" -is-generator-fn@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" - integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== - is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" @@ -4058,11 +3705,6 @@ is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.3: dependencies: call-bind "^1.0.7" -is-stream@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" - integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== - is-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" @@ -4075,10 +3717,12 @@ is-string@^1.0.5, is-string@^1.0.7: dependencies: has-tostringtag "^1.0.0" -is-subset@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-subset/-/is-subset-0.1.1.tgz#8a59117d932de1de00f245fcdd39ce43f1e939a6" - integrity sha512-6Ybun0IkarhmEqxXCNw/C0bna6Zb/TkfUX9UbwJtK6ObwAVCxmAP308WWTHviM/zAqXk05cdhYsUsZeGQh99iw== +is-subset-of@^3.1.10: + version "3.1.10" + resolved "https://registry.yarnpkg.com/is-subset-of/-/is-subset-of-3.1.10.tgz#d3f4331b9ca288318fae92ad5d953241b6f7b00b" + integrity sha512-avvaYgVmYWyaZ1NDFiv4y9JGkrE2je3op1Po4VYKKJKR8H2qVPsg1GZuuXl5elCTxTlwAIsrAjWAs4BVrISFRw== + dependencies: + typedescriptor "3.0.2" is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.4" @@ -4111,59 +3755,6 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== -istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" - integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== - -istanbul-lib-instrument@^5.0.4: - version "5.2.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" - integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== - dependencies: - "@babel/core" "^7.12.3" - "@babel/parser" "^7.14.7" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-coverage "^3.2.0" - semver "^6.3.0" - -istanbul-lib-instrument@^6.0.0: - version "6.0.3" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz#fa15401df6c15874bcb2105f773325d78c666765" - integrity sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q== - dependencies: - "@babel/core" "^7.23.9" - "@babel/parser" "^7.23.9" - "@istanbuljs/schema" "^0.1.3" - istanbul-lib-coverage "^3.2.0" - semver "^7.5.4" - -istanbul-lib-report@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" - integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== - dependencies: - istanbul-lib-coverage "^3.0.0" - make-dir "^4.0.0" - supports-color "^7.1.0" - -istanbul-lib-source-maps@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" - integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== - dependencies: - debug "^4.1.1" - istanbul-lib-coverage "^3.0.0" - source-map "^0.6.1" - -istanbul-reports@^3.1.3: - version "3.1.7" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.7.tgz#daed12b9e1dca518e15c056e1e537e741280fa0b" - integrity sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g== - dependencies: - html-escaper "^2.0.0" - istanbul-lib-report "^3.0.0" - jackspeak@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-4.0.1.tgz#9fca4ce961af6083e259c376e9e3541431f5287b" @@ -4173,86 +3764,6 @@ jackspeak@^4.0.1: optionalDependencies: "@pkgjs/parseargs" "^0.11.0" -jest-changed-files@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" - integrity sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w== - dependencies: - execa "^5.0.0" - jest-util "^29.7.0" - p-limit "^3.1.0" - -jest-circus@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a" - integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw== - dependencies: - "@jest/environment" "^29.7.0" - "@jest/expect" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - dedent "^1.0.0" - is-generator-fn "^2.0.0" - jest-each "^29.7.0" - jest-matcher-utils "^29.7.0" - jest-message-util "^29.7.0" - jest-runtime "^29.7.0" - jest-snapshot "^29.7.0" - jest-util "^29.7.0" - p-limit "^3.1.0" - pretty-format "^29.7.0" - pure-rand "^6.0.0" - slash "^3.0.0" - stack-utils "^2.0.3" - -jest-cli@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995" - integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg== - dependencies: - "@jest/core" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/types" "^29.6.3" - chalk "^4.0.0" - create-jest "^29.7.0" - exit "^0.1.2" - import-local "^3.0.2" - jest-config "^29.7.0" - jest-util "^29.7.0" - jest-validate "^29.7.0" - yargs "^17.3.1" - -jest-config@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f" - integrity sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ== - dependencies: - "@babel/core" "^7.11.6" - "@jest/test-sequencer" "^29.7.0" - "@jest/types" "^29.6.3" - babel-jest "^29.7.0" - chalk "^4.0.0" - ci-info "^3.2.0" - deepmerge "^4.2.2" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-circus "^29.7.0" - jest-environment-node "^29.7.0" - jest-get-type "^29.6.3" - jest-regex-util "^29.6.3" - jest-resolve "^29.7.0" - jest-runner "^29.7.0" - jest-util "^29.7.0" - jest-validate "^29.7.0" - micromatch "^4.0.4" - parse-json "^5.2.0" - pretty-format "^29.7.0" - slash "^3.0.0" - strip-json-comments "^3.1.1" - jest-diff@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-28.1.3.tgz#948a192d86f4e7a64c5264ad4da4877133d8792f" @@ -4263,102 +3774,11 @@ jest-diff@^28.1.3: jest-get-type "^28.0.2" pretty-format "^28.1.3" -jest-diff@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" - integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== - dependencies: - chalk "^4.0.0" - diff-sequences "^29.6.3" - jest-get-type "^29.6.3" - pretty-format "^29.7.0" - -jest-docblock@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a" - integrity sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g== - dependencies: - detect-newline "^3.0.0" - -jest-each@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" - integrity sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ== - dependencies: - "@jest/types" "^29.6.3" - chalk "^4.0.0" - jest-get-type "^29.6.3" - jest-util "^29.7.0" - pretty-format "^29.7.0" - -jest-environment-jsdom@^29.0.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz#d206fa3551933c3fd519e5dfdb58a0f5139a837f" - integrity sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA== - dependencies: - "@jest/environment" "^29.7.0" - "@jest/fake-timers" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/jsdom" "^20.0.0" - "@types/node" "*" - jest-mock "^29.7.0" - jest-util "^29.7.0" - jsdom "^20.0.0" - -jest-environment-node@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" - integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== - dependencies: - "@jest/environment" "^29.7.0" - "@jest/fake-timers" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - jest-mock "^29.7.0" - jest-util "^29.7.0" - jest-get-type@^28.0.2: version "28.0.2" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-28.0.2.tgz#34622e628e4fdcd793d46db8a242227901fcf203" integrity sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA== -jest-get-type@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" - integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== - -jest-haste-map@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" - integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA== - dependencies: - "@jest/types" "^29.6.3" - "@types/graceful-fs" "^4.1.3" - "@types/node" "*" - anymatch "^3.0.3" - fb-watchman "^2.0.0" - graceful-fs "^4.2.9" - jest-regex-util "^29.6.3" - jest-util "^29.7.0" - jest-worker "^29.7.0" - micromatch "^4.0.4" - walker "^1.0.8" - optionalDependencies: - fsevents "^2.3.2" - -jest-leak-detector@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728" - integrity sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw== - dependencies: - jest-get-type "^29.6.3" - pretty-format "^29.7.0" - -jest-localstorage-mock@^2.4.6: - version "2.4.26" - resolved "https://registry.yarnpkg.com/jest-localstorage-mock/-/jest-localstorage-mock-2.4.26.tgz#7d57fb3555f2ed5b7ed16fd8423fd81f95e9e8db" - integrity sha512-owAJrYnjulVlMIXOYQIPRCCn3MmqI3GzgfZCXdD3/pmwrIvFMXcKVWZ+aMc44IzaASapg0Z4SEFxR+v5qxDA2w== - jest-matcher-utils@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz#5a77f1c129dd5ba3b4d7fc20728806c78893146e" @@ -4369,16 +3789,6 @@ jest-matcher-utils@^28.1.3: jest-get-type "^28.0.2" pretty-format "^28.1.3" -jest-matcher-utils@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" - integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== - dependencies: - chalk "^4.0.0" - jest-diff "^29.7.0" - jest-get-type "^29.6.3" - pretty-format "^29.7.0" - jest-message-util@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-28.1.3.tgz#232def7f2e333f1eecc90649b5b94b0055e7c43d" @@ -4394,144 +3804,6 @@ jest-message-util@^28.1.3: slash "^3.0.0" stack-utils "^2.0.3" -jest-message-util@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" - integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== - dependencies: - "@babel/code-frame" "^7.12.13" - "@jest/types" "^29.6.3" - "@types/stack-utils" "^2.0.0" - chalk "^4.0.0" - graceful-fs "^4.2.9" - micromatch "^4.0.4" - pretty-format "^29.7.0" - slash "^3.0.0" - stack-utils "^2.0.3" - -jest-mock@^29.0.0, jest-mock@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" - integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw== - dependencies: - "@jest/types" "^29.6.3" - "@types/node" "*" - jest-util "^29.7.0" - -jest-pnp-resolver@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" - integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== - -jest-regex-util@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" - integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== - -jest-resolve-dependencies@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428" - integrity sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA== - dependencies: - jest-regex-util "^29.6.3" - jest-snapshot "^29.7.0" - -jest-resolve@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30" - integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== - dependencies: - chalk "^4.0.0" - graceful-fs "^4.2.9" - jest-haste-map "^29.7.0" - jest-pnp-resolver "^1.2.2" - jest-util "^29.7.0" - jest-validate "^29.7.0" - resolve "^1.20.0" - resolve.exports "^2.0.0" - slash "^3.0.0" - -jest-runner@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e" - integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ== - dependencies: - "@jest/console" "^29.7.0" - "@jest/environment" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - chalk "^4.0.0" - emittery "^0.13.1" - graceful-fs "^4.2.9" - jest-docblock "^29.7.0" - jest-environment-node "^29.7.0" - jest-haste-map "^29.7.0" - jest-leak-detector "^29.7.0" - jest-message-util "^29.7.0" - jest-resolve "^29.7.0" - jest-runtime "^29.7.0" - jest-util "^29.7.0" - jest-watcher "^29.7.0" - jest-worker "^29.7.0" - p-limit "^3.1.0" - source-map-support "0.5.13" - -jest-runtime@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817" - integrity sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ== - dependencies: - "@jest/environment" "^29.7.0" - "@jest/fake-timers" "^29.7.0" - "@jest/globals" "^29.7.0" - "@jest/source-map" "^29.6.3" - "@jest/test-result" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - chalk "^4.0.0" - cjs-module-lexer "^1.0.0" - collect-v8-coverage "^1.0.0" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-haste-map "^29.7.0" - jest-message-util "^29.7.0" - jest-mock "^29.7.0" - jest-regex-util "^29.6.3" - jest-resolve "^29.7.0" - jest-snapshot "^29.7.0" - jest-util "^29.7.0" - slash "^3.0.0" - strip-bom "^4.0.0" - -jest-snapshot@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" - integrity sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw== - dependencies: - "@babel/core" "^7.11.6" - "@babel/generator" "^7.7.2" - "@babel/plugin-syntax-jsx" "^7.7.2" - "@babel/plugin-syntax-typescript" "^7.7.2" - "@babel/types" "^7.3.3" - "@jest/expect-utils" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" - babel-preset-current-node-syntax "^1.0.0" - chalk "^4.0.0" - expect "^29.7.0" - graceful-fs "^4.2.9" - jest-diff "^29.7.0" - jest-get-type "^29.6.3" - jest-matcher-utils "^29.7.0" - jest-message-util "^29.7.0" - jest-util "^29.7.0" - natural-compare "^1.4.0" - pretty-format "^29.7.0" - semver "^7.5.3" - jest-util@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-28.1.3.tgz#f4f932aa0074f0679943220ff9cbba7e497028b0" @@ -4544,64 +3816,6 @@ jest-util@^28.1.3: graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-util@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" - integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== - dependencies: - "@jest/types" "^29.6.3" - "@types/node" "*" - chalk "^4.0.0" - ci-info "^3.2.0" - graceful-fs "^4.2.9" - picomatch "^2.2.3" - -jest-validate@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c" - integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw== - dependencies: - "@jest/types" "^29.6.3" - camelcase "^6.2.0" - chalk "^4.0.0" - jest-get-type "^29.6.3" - leven "^3.1.0" - pretty-format "^29.7.0" - -jest-watcher@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2" - integrity sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g== - dependencies: - "@jest/test-result" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - emittery "^0.13.1" - jest-util "^29.7.0" - string-length "^4.0.1" - -jest-worker@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" - integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== - dependencies: - "@types/node" "*" - jest-util "^29.7.0" - merge-stream "^2.0.0" - supports-color "^8.0.0" - -jest@^29.0.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" - integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== - dependencies: - "@jest/core" "^29.7.0" - "@jest/types" "^29.6.3" - import-local "^3.0.2" - jest-cli "^29.7.0" - jiti@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/jiti/-/jiti-2.4.2.tgz#d19b7732ebb6116b06e2038da74a55366faef560" @@ -4617,14 +3831,6 @@ js-tokens@^4.0.0: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@^3.13.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - js-yaml@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" @@ -4637,42 +3843,32 @@ jsdoc-type-pratt-parser@~4.1.0: resolved "https://registry.yarnpkg.com/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.1.0.tgz#ff6b4a3f339c34a6c188cbf50a16087858d22113" integrity sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg== -jsdom@^20.0.0: - version "20.0.3" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-20.0.3.tgz#886a41ba1d4726f67a8858028c99489fed6ad4db" - integrity sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ== - dependencies: - abab "^2.0.6" - acorn "^8.8.1" - acorn-globals "^7.0.0" - cssom "^0.5.0" - cssstyle "^2.3.0" - data-urls "^3.0.2" - decimal.js "^10.4.2" - domexception "^4.0.0" - escodegen "^2.0.0" - form-data "^4.0.0" - html-encoding-sniffer "^3.0.0" - http-proxy-agent "^5.0.0" - https-proxy-agent "^5.0.1" +jsdom@^26.0.0: + version "26.0.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-26.0.0.tgz#446dd1ad8cfc50df7e714e58f1f972c1763b354c" + integrity sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw== + dependencies: + cssstyle "^4.2.1" + data-urls "^5.0.0" + decimal.js "^10.4.3" + form-data "^4.0.1" + html-encoding-sniffer "^4.0.0" + http-proxy-agent "^7.0.2" + https-proxy-agent "^7.0.6" is-potential-custom-element-name "^1.0.1" - nwsapi "^2.2.2" - parse5 "^7.1.1" + nwsapi "^2.2.16" + parse5 "^7.2.1" + rrweb-cssom "^0.8.0" saxes "^6.0.0" symbol-tree "^3.2.4" - tough-cookie "^4.1.2" - w3c-xmlserializer "^4.0.0" + tough-cookie "^5.0.0" + w3c-xmlserializer "^5.0.0" webidl-conversions "^7.0.0" - whatwg-encoding "^2.0.0" - whatwg-mimetype "^3.0.0" - whatwg-url "^11.0.0" - ws "^8.11.0" - xml-name-validator "^4.0.0" - -jsesc@^2.5.1: - version "2.5.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + whatwg-encoding "^3.1.1" + whatwg-mimetype "^4.0.0" + whatwg-url "^14.1.0" + ws "^8.18.0" + xml-name-validator "^5.0.0" jsesc@^3.0.2: version "3.1.0" @@ -4738,11 +3934,6 @@ keyv@^4.5.3: dependencies: json-buffer "3.0.1" -kleur@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" - integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== - knip@^5.0.0: version "5.43.6" resolved "https://registry.yarnpkg.com/knip/-/knip-5.43.6.tgz#7faed2462ddc27e5b2f47a9224b7f51150b71f9b" @@ -4765,11 +3956,6 @@ knip@^5.0.0: zod "^3.22.4" zod-validation-error "^3.0.3" -leven@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" - integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== - levn@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" @@ -4842,21 +4028,11 @@ lodash.debounce@^4.0.8: resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== -lodash.isequal@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" - integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== - lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash.sortby@^4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" - integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA== - log-update@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/log-update/-/log-update-6.1.0.tgz#1a04ff38166f94647ae1af562f4bd6a15b1b7cd4" @@ -4873,6 +4049,16 @@ loglevel@^1.7.1: resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.9.2.tgz#c2e028d6c757720107df4e64508530db6621ba08" integrity sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg== +loupe@^3.1.0, loupe@^3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.1.3.tgz#042a8f7986d77f3d0f98ef7990a2b2fef18b0fd2" + integrity sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug== + +lru-cache@^10.4.3: + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + lru-cache@^11.0.0: version "11.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.0.0.tgz#15d93a196f189034d7166caf9fe55e7384c98a21" @@ -4890,6 +4076,13 @@ lunr@^2.3.9: resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow== +magic-string@^0.30.17: + version "0.30.17" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453" + integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.0" + make-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" @@ -4898,25 +4091,11 @@ make-dir@^2.1.0: pify "^4.0.1" semver "^5.6.0" -make-dir@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" - integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== - dependencies: - semver "^7.5.3" - make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== -makeerror@1.0.12: - version "1.0.12" - resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" - integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== - dependencies: - tmpl "1.0.5" - markdown-it@^14.1.0: version "14.1.0" resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-14.1.0.tgz#3c3c5992883c633db4714ccb4d7b5935d98b7d45" @@ -4959,7 +4138,7 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -merge2@^1.3.0: +merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== @@ -4984,11 +4163,6 @@ mime-types@^2.1.12: dependencies: mime-db "1.52.0" -mimic-fn@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== - mimic-fn@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" @@ -5035,16 +4209,21 @@ minipass@^7.1.2: resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== -mkdirp@1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== +mrmime@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-2.0.0.tgz#151082a6e06e59a9a39b46b3e14d5cfe92b3abb4" + integrity sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw== ms@^2.1.1, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +nanoid@^3.3.8: + version "3.3.8" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" + integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -5057,11 +4236,6 @@ node-fetch@^2.7.0: dependencies: whatwg-url "^5.0.0" -node-int64@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" - integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== - node-releases@^2.0.19: version "2.0.19" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" @@ -5082,13 +4256,6 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -npm-run-path@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" - integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== - dependencies: - path-key "^3.0.0" - npm-run-path@^5.1.0: version "5.3.0" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.3.0.tgz#e23353d0ebb9317f174e93417e4a4d82d0249e9f" @@ -5096,10 +4263,10 @@ npm-run-path@^5.1.0: dependencies: path-key "^4.0.0" -nwsapi@^2.2.2: - version "2.2.10" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.10.tgz#0b77a68e21a0b483db70b11fad055906e867cda8" - integrity sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ== +nwsapi@^2.2.16: + version "2.2.16" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.16.tgz#177760bba02c351df1d2644e220c31dfec8cdb43" + integrity sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ== object-inspect@^1.13.1: version "1.13.2" @@ -5163,13 +4330,6 @@ once@^1.3.0: dependencies: wrappy "1" -onetime@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" - integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== - dependencies: - mimic-fn "^2.1.0" - onetime@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" @@ -5203,7 +4363,7 @@ p-limit@^2.2.0: dependencies: p-try "^2.0.0" -p-limit@^3.0.2, p-limit@^3.1.0: +p-limit@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== @@ -5264,7 +4424,7 @@ parse-imports@^2.1.1: es-module-lexer "^1.5.3" slashes "^3.0.12" -parse-json@^5.0.0, parse-json@^5.2.0: +parse-json@^5.0.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== @@ -5279,12 +4439,12 @@ parse-ms@^4.0.0: resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-4.0.0.tgz#c0c058edd47c2a590151a718990533fd62803df4" integrity sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw== -parse5@^7.0.0, parse5@^7.1.1: - version "7.1.2" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" - integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== +parse5@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.2.1.tgz#8928f55915e6125f430cc44309765bf17556a33a" + integrity sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ== dependencies: - entities "^4.4.0" + entities "^4.5.0" path-exists@^4.0.0: version "4.0.0" @@ -5296,7 +4456,7 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== -path-key@^3.0.0, path-key@^3.1.0: +path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== @@ -5319,10 +4479,20 @@ path-scurry@^2.0.0: lru-cache "^11.0.0" minipass "^7.1.2" -path-to-regexp@^2.2.1: - version "2.4.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-2.4.0.tgz#35ce7f333d5616f1c1e1bfe266c3aba2e5b2e704" - integrity sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w== +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +pathe@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/pathe/-/pathe-2.0.2.tgz#5ed86644376915b3c7ee4d00ac8c348d671da3a5" + integrity sha512-15Ztpk+nov8DR524R4BF7uEuzESgzUEAV4Ah7CUMNGXdE5ELuvxElxGXndBl32vMSsWa1jpNf22Z+Er3sKwq+w== + +pathval@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.0.tgz#7e2550b422601d4f6b8e26f1301bc8f15a741a25" + integrity sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA== picocolors@^1.0.0, picocolors@^1.1.0, picocolors@^1.1.1: version "1.1.1" @@ -5349,18 +4519,6 @@ pify@^4.0.1: resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== -pirates@^4.0.4: - version "4.0.6" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" - integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== - -pkg-dir@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" - integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== - dependencies: - find-up "^4.0.0" - pluralize@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" @@ -5371,6 +4529,15 @@ possible-typed-array-names@^1.0.0: resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== +postcss@^8.4.49: + version "8.5.1" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.1.tgz#e2272a1f8a807fafa413218245630b5db10a3214" + integrity sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ== + dependencies: + nanoid "^3.3.8" + picocolors "^1.1.1" + source-map-js "^1.2.1" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -5391,15 +4558,6 @@ pretty-format@^28.1.3: ansi-styles "^5.0.0" react-is "^18.0.0" -pretty-format@^29.0.0, pretty-format@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" - integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== - dependencies: - "@jest/schemas" "^29.6.3" - ansi-styles "^5.0.0" - react-is "^18.0.0" - pretty-ms@^9.0.0: version "9.2.0" resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-9.2.0.tgz#e14c0aad6493b69ed63114442a84133d7e560ef0" @@ -5407,56 +4565,16 @@ pretty-ms@^9.0.0: dependencies: parse-ms "^4.0.0" -prompts@^2.0.1: - version "2.4.2" - resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" - integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== - dependencies: - kleur "^3.0.3" - sisteransi "^1.0.5" - -psl@^1.1.33: - version "1.9.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" - integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== - punycode.js@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode.js/-/punycode.js-2.3.1.tgz#6b53e56ad75588234e79f4affa90972c7dd8cdb7" integrity sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA== -punycode@^2.1.0, punycode@^2.1.1: +punycode@^2.1.0, punycode@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== -pure-rand@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" - integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== - -pvtsutils@^1.3.2, pvtsutils@^1.3.5: - version "1.3.5" - resolved "https://registry.yarnpkg.com/pvtsutils/-/pvtsutils-1.3.5.tgz#b8705b437b7b134cd7fd858f025a23456f1ce910" - integrity sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA== - dependencies: - tslib "^2.6.1" - -pvutils@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/pvutils/-/pvutils-1.1.3.tgz#f35fc1d27e7cd3dfbd39c0826d173e806a03f5a3" - integrity sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ== - -querystring@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.1.tgz#40d77615bb09d16902a85c3e38aa8b5ed761c2dd" - integrity sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg== - -querystringify@^2.1.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" - integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== - queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -5573,49 +4691,22 @@ regjsparser@^0.12.0: dependencies: jsesc "~3.0.2" -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== - require-from-string@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== - -resolve-cwd@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" - integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== - dependencies: - resolve-from "^5.0.0" - resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== -resolve-from@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" - integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== - resolve-pkg-maps@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== -resolve.exports@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" - integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== - -resolve@^1.10.0, resolve@^1.10.1, resolve@^1.20.0, resolve@^1.22.4, resolve@~1.22.2: +resolve@^1.10.0, resolve@^1.10.1, resolve@^1.22.4, resolve@~1.22.2: version "1.22.8" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== @@ -5671,6 +4762,39 @@ rimraf@^6.0.0: glob "^11.0.0" package-json-from-dist "^1.0.0" +rollup@^4.23.0: + version "4.34.3" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.34.3.tgz#319a3c3065d9f80fef2faef24ef8aba7773e7f77" + integrity sha512-ORCtU0UBJyiAIn9m0llUXJXAswG/68pZptCrqxHG7//Z2DDzAUeyyY5hqf4XrsGlUxscMr9GkQ2QI7KTLqeyPw== + dependencies: + "@types/estree" "1.0.6" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.34.3" + "@rollup/rollup-android-arm64" "4.34.3" + "@rollup/rollup-darwin-arm64" "4.34.3" + "@rollup/rollup-darwin-x64" "4.34.3" + "@rollup/rollup-freebsd-arm64" "4.34.3" + "@rollup/rollup-freebsd-x64" "4.34.3" + "@rollup/rollup-linux-arm-gnueabihf" "4.34.3" + "@rollup/rollup-linux-arm-musleabihf" "4.34.3" + "@rollup/rollup-linux-arm64-gnu" "4.34.3" + "@rollup/rollup-linux-arm64-musl" "4.34.3" + "@rollup/rollup-linux-loongarch64-gnu" "4.34.3" + "@rollup/rollup-linux-powerpc64le-gnu" "4.34.3" + "@rollup/rollup-linux-riscv64-gnu" "4.34.3" + "@rollup/rollup-linux-s390x-gnu" "4.34.3" + "@rollup/rollup-linux-x64-gnu" "4.34.3" + "@rollup/rollup-linux-x64-musl" "4.34.3" + "@rollup/rollup-win32-arm64-msvc" "4.34.3" + "@rollup/rollup-win32-ia32-msvc" "4.34.3" + "@rollup/rollup-win32-x64-msvc" "4.34.3" + fsevents "~2.3.2" + +rrweb-cssom@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz#3021d1b4352fbf3b614aaeed0bc0d5739abe0bc2" + integrity sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw== + run-parallel@^1.1.9, run-parallel@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" @@ -5724,16 +4848,11 @@ sdp-transform@^2.14.1: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@^6.1.0, semver@^6.3.0, semver@^6.3.1: +semver@^6.1.0, semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.5.3, semver@^7.5.4: - version "7.6.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" - integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== - semver@^7.6.0, semver@^7.6.3: version "7.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f" @@ -5783,20 +4902,24 @@ side-channel@^1.0.4: get-intrinsic "^1.2.4" object-inspect "^1.13.1" -signal-exit@^3.0.3, signal-exit@^3.0.7: - version "3.0.7" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" - integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +siginfo@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30" + integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== signal-exit@^4.0.1, signal-exit@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== -sisteransi@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" - integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== +sirv@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/sirv/-/sirv-3.0.0.tgz#f8d90fc528f65dff04cb597a88609d4e8a4361ce" + integrity sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg== + dependencies: + "@polka/url" "^1.0.0-next.24" + mrmime "^2.0.0" + totalist "^3.0.0" slash@^2.0.0: version "2.0.0" @@ -5834,18 +4957,10 @@ smol-toml@^1.3.1: resolved "https://registry.yarnpkg.com/smol-toml/-/smol-toml-1.3.1.tgz#d9084a9e212142e3cab27ef4e2b8e8ba620bfe15" integrity sha512-tEYNll18pPKHroYSmLLrksq233j021G0giwW7P3D24jC54pQ5W5BXMsQ/Mvw1OJCmEYDgY+lrzT+3nNUtoNfXQ== -source-map-support@0.5.13: - version "0.5.13" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" - integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +source-map-js@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== spdx-correct@^3.0.0: version "3.2.0" @@ -5881,11 +4996,6 @@ spdx-license-ids@^3.0.0: resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz#6d6e980c9df2b6fc905343a3b2d702a6239536c3" integrity sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg== -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== - stable-hash@^0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/stable-hash/-/stable-hash-0.0.4.tgz#55ae7dadc13e4b3faed13601587cec41859b42f7" @@ -5898,19 +5008,21 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" +stackback@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b" + integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== + +std-env@^3.8.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.8.0.tgz#b56ffc1baf1a29dcc80a3bdf11d7fca7c315e7d5" + integrity sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w== + string-argv@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6" integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== -string-length@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" - integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== - dependencies: - char-regex "^1.0.2" - strip-ansi "^6.0.0" - "string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -5920,7 +5032,7 @@ string-length@^4.0.1: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +string-width@^4.1.0: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -6001,16 +5113,6 @@ strip-bom@^3.0.0: resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== -strip-bom@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" - integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== - -strip-final-newline@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" - integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== - strip-final-newline@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" @@ -6052,13 +5154,6 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -supports-color@^8.0.0: - version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" - supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" @@ -6082,29 +5177,55 @@ tapable@^2.2.0: resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== -test-exclude@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" - integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== - dependencies: - "@istanbuljs/schema" "^0.1.2" - glob "^7.1.4" - minimatch "^3.0.4" - text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== -tmpl@1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" - integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== +tinybench@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b" + integrity sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg== + +tinyexec@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.2.tgz#941794e657a85e496577995c6eef66f53f42b3d2" + integrity sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA== + +tinyglobby@^0.2.10: + version "0.2.10" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.10.tgz#e712cf2dc9b95a1f5c5bbd159720e15833977a0f" + integrity sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew== + dependencies: + fdir "^6.4.2" + picomatch "^4.0.2" + +tinypool@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.0.2.tgz#706193cc532f4c100f66aa00b01c42173d9051b2" + integrity sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA== -to-fast-properties@^2.0.0: +tinyrainbow@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== + resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-2.0.0.tgz#9509b2162436315e80e3eee0fcce4474d2444294" + integrity sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw== + +tinyspy@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-3.0.2.tgz#86dd3cf3d737b15adcf17d7887c84a75201df20a" + integrity sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q== + +tldts-core@^6.1.76: + version "6.1.76" + resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-6.1.76.tgz#1ea632bbf11b288645dd7e2f3fb9cb6fa41e6bd7" + integrity sha512-uzhJ02RaMzgQR3yPoeE65DrcHI6LoM4saUqXOt/b5hmb3+mc4YWpdSeAQqVqRUlQ14q8ZuLRWyBR1ictK1dzzg== + +tldts@^6.1.32: + version "6.1.76" + resolved "https://registry.yarnpkg.com/tldts/-/tldts-6.1.76.tgz#c8b60fba55ca78e1c228bead4f69d18731526079" + integrity sha512-6U2ti64/nppsDxQs9hw8ephA3nO6nSQvVVfxwRw8wLQPFtLI1cFI1a1eP22g+LUP+1TA2pKKjUTwWB+K2coqmQ== + dependencies: + tldts-core "^6.1.76" to-regex-range@^5.0.1: version "5.0.1" @@ -6113,35 +5234,35 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -tough-cookie@^4.1.2: - version "4.1.4" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.4.tgz#945f1461b45b5a8c76821c33ea49c3ac192c1b36" - integrity sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag== - dependencies: - psl "^1.1.33" - punycode "^2.1.1" - universalify "^0.2.0" - url-parse "^1.5.3" +totalist@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/totalist/-/totalist-3.0.1.tgz#ba3a3d600c915b1a97872348f79c127475f6acf8" + integrity sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ== -tr46@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" - integrity sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA== +tough-cookie@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-5.1.0.tgz#0667b0f2fbb5901fe6f226c3e0b710a9a4292f87" + integrity sha512-rvZUv+7MoBYTiDmFPBrhL7Ujx9Sk+q9wwm22x8c8T5IJaR+Wsyc7TNxbVxo84kZoRJZZMazowFLqpankBEQrGg== dependencies: - punycode "^2.1.0" + tldts "^6.1.32" -tr46@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9" - integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA== +tr46@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-5.0.0.tgz#3b46d583613ec7283020d79019f1335723801cec" + integrity sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g== dependencies: - punycode "^2.1.1" + punycode "^2.3.1" tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== +ts-api-utils@^1.3.0: + version "1.4.3" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.3.tgz#bfc2215fe6528fecab2b0fba570a2e8a4263b064" + integrity sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw== + ts-api-utils@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.0.0.tgz#b9d7d5f7ec9f736f4d0f09758b8607979044a900" @@ -6181,11 +5302,6 @@ tsconfig-paths@^3.15.0: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^2.0.0, tslib@^2.4.0, tslib@^2.6.1: - version "2.6.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" - integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== - tslib@^2.6.2: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" @@ -6198,21 +5314,11 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" -type-detect@4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" - integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== - type-fest@^0.20.2: version "0.20.2" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== -type-fest@^0.21.3: - version "0.21.3" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" - integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== - type-fest@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" @@ -6267,6 +5373,11 @@ typed-array-length@^1.0.6: is-typed-array "^1.1.13" possible-typed-array-names "^1.0.0" +typedescriptor@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/typedescriptor/-/typedescriptor-3.0.2.tgz#9ad1715bc2be1cf063d5acbc4cd4bfc96d644225" + integrity sha512-hyVbaCUd18UiXk656g/imaBLMogpdijIEpnhWYrSda9rhvO4gOU16n2nh7xG5lv/rjumnZzGOdz0CEGTmFe0fQ== + typedoc-plugin-coverage@^3.0.0: version "3.4.1" resolved "https://registry.yarnpkg.com/typedoc-plugin-coverage/-/typedoc-plugin-coverage-3.4.1.tgz#13b445cecb674845945e218c4560bbd91299af83" @@ -6346,11 +5457,6 @@ unicode-property-aliases-ecmascript@^2.0.0: resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== -universalify@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" - integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== - update-browserslist-db@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz#97e9c96ab0ae7bcac08e9ae5151d26e6bc6b5580" @@ -6366,38 +5472,16 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -url-parse@^1.5.3: - version "1.5.10" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" - integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== - dependencies: - querystringify "^2.1.1" - requires-port "^1.0.0" - uuid@11: version "11.0.5" resolved "https://registry.yarnpkg.com/uuid/-/uuid-11.0.5.tgz#07b46bdfa6310c92c3fb3953a8720f170427fc62" integrity sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA== -uuid@8.3.2: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== -v8-to-istanbul@^9.0.1: - version "9.3.0" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz#b9572abfa62bd556c16d75fdebc1a411d5ff3175" - integrity sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA== - dependencies: - "@jridgewell/trace-mapping" "^0.3.12" - "@types/istanbul-lib-coverage" "^2.0.1" - convert-source-map "^2.0.0" - validate-npm-package-license@^3.0.1: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" @@ -6406,19 +5490,65 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" -w3c-xmlserializer@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz#aebdc84920d806222936e3cdce408e32488a3073" - integrity sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw== +vite-node@3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-3.0.5.tgz#6a0d06f7a4bdaae6ddcdedc12d910d886cf7d62f" + integrity sha512-02JEJl7SbtwSDJdYS537nU6l+ktdvcREfLksk/NDAqtdKWGqHl+joXzEubHROmS3E6pip+Xgu2tFezMu75jH7A== dependencies: - xml-name-validator "^4.0.0" + cac "^6.7.14" + debug "^4.4.0" + es-module-lexer "^1.6.0" + pathe "^2.0.2" + vite "^5.0.0 || ^6.0.0" + +"vite@^5.0.0 || ^6.0.0": + version "6.0.11" + resolved "https://registry.yarnpkg.com/vite/-/vite-6.0.11.tgz#224497e93e940b34c3357c9ebf2ec20803091ed8" + integrity sha512-4VL9mQPKoHy4+FE0NnRE/kbY51TOfaknxAjt3fJbGJxhIpBZiqVzlZDEesWWsuREXHwNdAoOFZ9MkPEVXczHwg== + dependencies: + esbuild "^0.24.2" + postcss "^8.4.49" + rollup "^4.23.0" + optionalDependencies: + fsevents "~2.3.3" -walker@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" - integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== +vitest-sonar-reporter@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/vitest-sonar-reporter/-/vitest-sonar-reporter-2.0.0.tgz#6372e5faba2b2834eac0b2cd1283263d38d0e718" + integrity sha512-LorC3NnmrBrryx4+l3BEsNQjD0Y7wfmrD1y/+tHDuZUuVj7w8nOxRXCBSppDfmgfpToOhwchh0JcL4IGMKUKDA== + +vitest@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/vitest/-/vitest-3.0.5.tgz#a9a3fa1203d85869c9ba66f3ea990b72d00ddeb0" + integrity sha512-4dof+HvqONw9bvsYxtkfUp2uHsTN9bV2CZIi1pWgoFpL1Lld8LA1ka9q/ONSsoScAKG7NVGf2stJTI7XRkXb2Q== + dependencies: + "@vitest/expect" "3.0.5" + "@vitest/mocker" "3.0.5" + "@vitest/pretty-format" "^3.0.5" + "@vitest/runner" "3.0.5" + "@vitest/snapshot" "3.0.5" + "@vitest/spy" "3.0.5" + "@vitest/utils" "3.0.5" + chai "^5.1.2" + debug "^4.4.0" + expect-type "^1.1.0" + magic-string "^0.30.17" + pathe "^2.0.2" + std-env "^3.8.0" + tinybench "^2.9.0" + tinyexec "^0.3.2" + tinypool "^1.0.2" + tinyrainbow "^2.0.0" + vite "^5.0.0 || ^6.0.0" + vite-node "3.0.5" + why-is-node-running "^2.3.0" + +w3c-xmlserializer@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz#f925ba26855158594d907313cedd1476c5967f6c" + integrity sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA== dependencies: - makeerror "1.0.12" + xml-name-validator "^5.0.0" wcwidth@^1.0.1: version "1.0.1" @@ -6427,50 +5557,34 @@ wcwidth@^1.0.1: dependencies: defaults "^1.0.3" -webcrypto-core@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.8.0.tgz#aaea17f3dd9c77c304e3c494eb27ca07cc72ca37" - integrity sha512-kR1UQNH8MD42CYuLzvibfakG5Ew5seG85dMMoAM/1LqvckxaF6pUiidLuraIu4V+YCIFabYecUZAW0TuxAoaqw== - dependencies: - "@peculiar/asn1-schema" "^2.3.8" - "@peculiar/json-schema" "^1.1.12" - asn1js "^3.0.1" - pvtsutils "^1.3.5" - tslib "^2.6.2" - webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== -webidl-conversions@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" - integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== - webidl-conversions@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== -whatwg-encoding@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53" - integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg== +whatwg-encoding@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz#d0f4ef769905d426e1688f3e34381a99b60b76e5" + integrity sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ== dependencies: iconv-lite "0.6.3" -whatwg-mimetype@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" - integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== +whatwg-mimetype@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" + integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== -whatwg-url@^11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018" - integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ== +whatwg-url@^14.0.0, whatwg-url@^14.1.0: + version "14.1.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-14.1.0.tgz#fffebec86cc8e6c2a657e50dc606207b870f0ab3" + integrity sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w== dependencies: - tr46 "^3.0.0" + tr46 "^5.0.0" webidl-conversions "^7.0.0" whatwg-url@^5.0.0: @@ -6481,15 +5595,6 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" -whatwg-url@^6.5.0: - version "6.5.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8" - integrity sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ== - dependencies: - lodash.sortby "^4.7.0" - tr46 "^1.0.1" - webidl-conversions "^4.0.2" - which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" @@ -6519,6 +5624,14 @@ which@^2.0.1: dependencies: isexe "^2.0.0" +why-is-node-running@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz#a3f69a97107f494b3cdc3bdddd883a7d65cebf04" + integrity sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w== + dependencies: + siginfo "^2.0.0" + stackback "0.0.2" + word-wrap@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" @@ -6533,15 +5646,6 @@ word-wrap@^1.2.5: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" @@ -6565,39 +5669,21 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -write-file-atomic@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" - integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== - dependencies: - imurmurhash "^0.1.4" - signal-exit "^3.0.7" - -ws@^8.11.0: +ws@^8.18.0: version "8.18.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== -xml-name-validator@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" - integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== - -xml@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5" - integrity sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw== +xml-name-validator@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-5.0.0.tgz#82be9b957f7afdacf961e5980f1bf227c0bf7673" + integrity sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg== xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== -y18n@^5.0.5: - version "5.0.8" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" - integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== - yallist@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" @@ -6608,24 +5694,6 @@ yaml@^2.6.1, yaml@^2.7.0: resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.7.0.tgz#aef9bb617a64c937a9a748803786ad8d3ffe1e98" integrity sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA== -yargs-parser@^21.1.1: - version "21.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" - integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== - -yargs@^17.3.1: - version "17.7.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" - integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== - dependencies: - cliui "^8.0.1" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.3" - y18n "^5.0.5" - yargs-parser "^21.1.1" - yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"