Update fetch-mock-jest to @fetch-mock/jest (#31720)

* Remove tests which assert feature_oidc_native_flow=false behaviour, that setting is long gone

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Upgrade fetch-mock-jest to @fetch-mock/jest

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update yarn.lock

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Make knip happy

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Disable broken tests

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Fix shared-components tests

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Snapshots

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski 2026-01-15 09:21:25 +00:00 committed by GitHub
parent f5c6477ef7
commit 6f0cd7621b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
64 changed files with 372 additions and 556 deletions

View File

@ -11,7 +11,7 @@ import { env } from "process";
import type { Config } from "jest";
const config: Config = {
testEnvironment: "jsdom",
testEnvironment: "jest-fixed-jsdom",
testEnvironmentOptions: {
url: "http://localhost/",
// This is needed to be able to load dual CJS/ESM WASM packages e.g. rust crypto & matrix-wywiwyg
@ -39,7 +39,6 @@ const config: Config = {
"workers/(.+)Factory": "<rootDir>/__mocks__/workerFactoryMock.js",
"^!!raw-loader!.*": "jest-raw-loader",
"recorderWorkletFactory": "<rootDir>/__mocks__/empty.js",
"^fetch-mock$": "<rootDir>/node_modules/fetch-mock",
"counterpart": "<rootDir>/node_modules/counterpart",
},
transformIgnorePatterns: [

View File

@ -43,7 +43,8 @@ export default {
// Embedded into webapp
"@element-hq/element-call-embedded",
// Transitive dep of jest
"jsdom",
"@jest/globals",
"vitest-environment-jest-fixed-jsdom",
// Used by matrix-js-sdk, which means we have to include them as a
// dependency so that // we can run `tsc` (since we import the typescript

View File

@ -182,6 +182,7 @@
"@casualbot/jest-sonar-reporter": "2.5.0",
"@element-hq/element-call-embedded": "0.16.3",
"@element-hq/element-web-playwright-common": "2.2.3",
"@fetch-mock/jest": "^0.2.20",
"@peculiar/webcrypto": "^1.4.3",
"@playwright/test": "1.57.0",
"@principalstudio/html-webpack-inject-preload": "^1.2.7",
@ -210,7 +211,6 @@
"@types/minimist": "^1.2.5",
"@types/modernizr": "^3.5.3",
"@types/node": "18",
"@types/node-fetch": "^2.6.2",
"@types/pako": "^2.0.0",
"@types/qrcode": "^1.3.5",
"@types/react": "19.2.7",
@ -231,7 +231,6 @@
"chokidar": "^5.0.0",
"concurrently": "^9.0.0",
"copy-webpack-plugin": "^13.0.0",
"core-js": "^3.38.1",
"cronstrue": "^3.0.0",
"css-loader": "^7.0.0",
"css-minimizer-webpack-plugin": "^7.0.0",
@ -250,15 +249,14 @@
"eslint-plugin-unicorn": "^56.0.0",
"express": "^5.0.0",
"fake-indexeddb": "^6.0.0",
"fetch-mock": "9.11.0",
"fetch-mock-jest": "^1.5.1",
"file-loader": "^6.0.0",
"html-webpack-plugin": "^5.5.3",
"husky": "^9.0.0",
"identity-obj-proxy": "^3.0.0",
"jest": "^30.0.0",
"jest-canvas-mock": "^2.5.2",
"jest-environment-jsdom": "^30.0.0",
"jest-environment-jsdom": "^30.2.0",
"jest-fixed-jsdom": "^0.0.11",
"jest-mock": "^30.0.0",
"jest-raw-loader": "^1.0.1",
"jsqr": "^1.4.0",
@ -268,7 +266,6 @@
"mini-css-extract-plugin": "2.9.2",
"minimist": "^1.2.6",
"modernizr": "^3.12.0",
"node-fetch": "^2.6.7",
"patch-package": "^8.0.0",
"playwright-core": "^1.51.0",
"postcss": "8.4.46",

View File

@ -10,7 +10,7 @@ import { env } from "process";
import type { Config } from "jest";
const config: Config = {
testEnvironment: "jsdom",
testEnvironment: "jest-fixed-jsdom",
testEnvironmentOptions: {
url: "http://localhost/",
},

View File

@ -5,18 +5,19 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import { setLanguage } from "../../src/utils/i18n";
import en from "../i18n/strings/en_EN.json";
export function setupLanguageMock(): void {
fetchMock
.get("/i18n/languages.json", {
.get("end:/i18n/languages.json", {
en: "en_EN.json",
})
.get("end:en_EN.json", en);
}
setupLanguageMock();
fetchMock.mockGlobal();
setLanguage("en");

View File

@ -0,0 +1,17 @@
diff --git a/node_modules/jest-fixed-jsdom/index.js b/node_modules/jest-fixed-jsdom/index.js
index ac8033b..b1ba8f0 100644
--- a/node_modules/jest-fixed-jsdom/index.js
+++ b/node_modules/jest-fixed-jsdom/index.js
@@ -21,9 +21,10 @@ class FixedJSDOMEnvironment extends JSDOMEnvironment {
this.global.TextEncoderStream = TextEncoderStream
this.global.ReadableStream = ReadableStream
- this.global.Blob = Blob
+ // this.global.Blob = Blob
+ // this.global.File = File
this.global.Headers = Headers
- this.global.FormData = FormData
+ // this.global.FormData = FormData
this.global.Request = Request
this.global.Response = Response
this.global.fetch = fetch

View File

@ -699,6 +699,10 @@ async function handleLoadSessionFailure(e: unknown, loadSessionOpts?: ILoadSessi
* Also stops the old MatrixClient and clears old credentials/etc out of
* storage before starting the new client.
*
* This function does not work for OIDC login.
* Storage is cleared early in the process so the required data is lost.
* You must use {@link attemptDelegatedAuthLogin} followed by {@link restoreSessionFromStorage} for OIDC login.
*
* @param {IMatrixClientCreds} credentials The credentials to use
*
* @returns {Promise} promise which resolves to the new MatrixClient once it has been started

View File

@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import SdkConfig from "../../src/SdkConfig";
import PlatformPeg from "../../src/PlatformPeg";
@ -16,8 +16,6 @@ import WebPlatform from "../../src/vector/platform/WebPlatform";
/** The matrix versions our mock server claims to support */
const SERVER_SUPPORTED_MATRIX_VERSIONS = ["v1.1", "v1.5", "v1.6", "v1.8", "v1.9"];
fetchMock.config.overwriteRoutes = true;
describe("Loading server config", function () {
beforeEach(async () => {
SdkConfig.reset();

View File

@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details.
*/
import React from "react";
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import { render, type RenderResult, screen } from "jest-matrix-react";
import { WrapperLifecycle, type WrapperOpts } from "@matrix-org/react-sdk-module-api/lib/lifecycles/WrapperLifecycle";
@ -23,8 +23,6 @@ import { waitForLoadingSpinner, waitForWelcomeComponent } from "../test-utils";
/** The matrix versions our mock server claims to support */
const SERVER_SUPPORTED_MATRIX_VERSIONS = ["v1.1", "v1.5", "v1.6", "v1.8", "v1.9"];
fetchMock.config.overwriteRoutes = true;
describe("Wrapper", () => {
beforeEach(async () => {
SdkConfig.reset();

View File

@ -24,5 +24,5 @@ export const mocks = {
setSinkId: jest.fn(),
suspend: jest.fn(),
decodeAudioData: jest.fn(),
},
} as unknown as AudioContext,
};

View File

@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import { ModuleLoader } from "@element-hq/element-web-module-api";
import { merge } from "lodash";
@ -44,21 +44,29 @@ export function setupLanguageMock() {
const enTranslations = merge(enElementWeb, enSharedComponents);
const deTranslations = merge(deElementWeb, deSharedComponents);
fetchMock.mockGlobal();
fetchMock
.get("/i18n/languages.json", {
en: "en_EN.json",
de: "de_DE.json",
lv: "lv.json",
})
.get(
"end:/i18n/languages.json",
{
en: "en_EN.json",
de: "de_DE.json",
lv: "lv.json",
},
{ name: "languages" },
)
.get("end:en_EN.json", enTranslations)
.get("end:de_DE.json", deTranslations)
.get("end:lv.json", lv);
}
setupLanguageMock();
beforeEach(setupLanguageMock);
afterEach(() => fetchMock.callHistory.flush());
// Initialise the fetchMock before the test starts so the languageHandler.setLanguage call below can function
setupLanguageMock();
languageHandler.setLanguage("en");
languageHandler.setMissingEntryGenerator((key) => key.split("|", 2)[1]);
// Set up the mdule API (so the i18n API exists)
// Set up the module API (so the i18n API exists)
const moduleLoader = new ModuleLoader(ModuleApi.instance);
window.mxModuleLoader = moduleLoader;

View File

@ -6,9 +6,8 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import fetchMock from "fetch-mock-jest";
import { TextDecoder, TextEncoder } from "util";
import { Response } from "node-fetch";
import fetchMock, { manageFetchMockGlobally } from "@fetch-mock/jest";
import { jest } from "@jest/globals";
import { mocks } from "./mocks";
@ -46,7 +45,7 @@ window.ClipboardEvent = MyClipboardEvent as any;
// matchMedia is not included in jsdom
// TODO: Extract this to a function and have tests that need it opt into it.
const mockMatchMedia = (query: string) => ({
global.matchMedia = (query: string) => ({
matches: false,
media: query,
onchange: null,
@ -54,35 +53,39 @@ const mockMatchMedia = (query: string) => ({
removeListener: jest.fn(), // Deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
dispatchEvent: jest.fn<(event: Event) => boolean>(),
});
global.matchMedia = mockMatchMedia;
// maplibre requires a createObjectURL mock
global.URL.createObjectURL = jest.fn();
global.URL.createObjectURL = jest.fn((obj) => "blob");
global.URL.revokeObjectURL = jest.fn();
// polyfilling TextEncoder as it is not available on JSDOM
// view https://github.com/facebook/jest/issues/9983
// XXX: Node's implementation has marginally different types, so we fudge it
(globalThis as any).TextEncoder = TextEncoder;
// @ts-ignore
global.TextDecoder = TextDecoder;
// prevent errors whenever a component tries to manually scroll.
window.HTMLElement.prototype.scrollIntoView = jest.fn();
window.HTMLAudioElement.prototype.canPlayType = jest.fn((format) => (format === "audio/mpeg" ? "probably" : ""));
// set up fetch API mock
fetchMock.config.overwriteRoutes = false;
fetchMock.catch("");
fetchMock.get("/image-file-stub", "image file stub");
fetchMock.get("/_matrix/client/versions", {});
// @ts-ignore
window.fetch = fetchMock.sandbox();
function setupFileStubMocks() {
fetchMock.get("end:/image-file-stub", "image file stub", { sticky: true });
}
setupFileStubMocks();
// @ts-ignore
window.Response = Response;
beforeEach(() => {
// set up fetch API mock
fetchMock.hardReset();
fetchMock.catch(404);
setupFileStubMocks();
fetchMock.get("/_matrix/client/versions", {}, { sticky: true });
fetchMock.mockGlobal();
});
afterEach(() => {
fetchMock.removeRoutes();
window.sessionStorage?.clear();
window.localStorage?.clear();
});
fetchMock.config.allowRelativeUrls = true;
manageFetchMockGlobally(jest);
// set up AudioContext API mock
global.AudioContext = jest.fn().mockImplementation(() => ({ ...mocks.AudioContext }));
global.AudioContext = jest.fn<() => AudioContext>().mockImplementation(() => ({ ...mocks.AudioContext }));

View File

@ -8,17 +8,14 @@ Please see LICENSE files in the repository root for full details.
export const REPEATABLE_DATE = new Date(2022, 10, 17, 16, 58, 32, 517);
const RealDateTimeFormat = global.Intl.DateTimeFormat;
// allow setting default locale and set timezone
// defaults to en-GB / Europe/London
// so tests run the same everywhere
export const mockIntlDateTimeFormat = (defaultLocale = "en-GB", defaultTimezone = "Europe/London"): void => {
// unmock so we can use real DateTimeFormat in mockImplementation
if (jest.isMockFunction(global.Intl.DateTimeFormat)) {
unmockIntlDateTimeFormat();
}
const DateTimeFormat = Intl.DateTimeFormat;
jest.spyOn(global.Intl, "DateTimeFormat").mockImplementation(
(locale, options) => new DateTimeFormat(locale || defaultLocale, { ...options, timeZone: defaultTimezone }),
(locale, options) => new RealDateTimeFormat(locale || defaultLocale, { ...options, timeZone: defaultTimezone }),
);
};

View File

@ -20,7 +20,7 @@ import { CallEvent, CallState, CallType, MatrixCall } from "matrix-js-sdk/src/we
import EventEmitter from "events";
import { mocked } from "jest-mock";
import { CallEventHandlerEvent } from "matrix-js-sdk/src/webrtc/callEventHandler";
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import { waitFor } from "jest-matrix-react";
import { PushProcessor } from "matrix-js-sdk/src/pushprocessor";
@ -416,11 +416,7 @@ describe("LegacyCallHandler without third party protocols", () => {
audioElement.id = "remoteAudio";
document.body.appendChild(audioElement);
fetchMock.get(
"/media/ring.mp3",
{ body: new Blob(["1", "2", "3", "4"], { type: "audio/mpeg" }) },
{ sendAsJson: false },
);
fetchMock.get("end:/media/ring.mp3", { body: new Blob(["1", "2", "3", "4"], { type: "audio/mpeg" }) });
});
afterEach(() => {
@ -438,9 +434,9 @@ describe("LegacyCallHandler without third party protocols", () => {
it("should cache sounds between playbacks", async () => {
await callHandler.play(AudioID.Ring);
expect(mockAudioBufferSourceNode.start).toHaveBeenCalled();
expect(fetchMock.calls("/media/ring.mp3")).toHaveLength(1);
expect(fetchMock).toHaveFetchedTimes(1, "end:/media/ring.mp3");
await callHandler.play(AudioID.Ring);
expect(fetchMock.calls("/media/ring.mp3")).toHaveLength(1);
expect(fetchMock).toHaveFetchedTimes(1, "end:/media/ring.mp3");
});
it("should allow silencing an incoming call ring", async () => {

View File

@ -12,7 +12,7 @@ import * as MatrixJs from "matrix-js-sdk/src/matrix";
import { decodeBase64, encodeUnpaddedBase64 } from "matrix-js-sdk/src/matrix";
import * as encryptAESSecretStorageItemModule from "matrix-js-sdk/src/utils/encryptAESSecretStorageItem";
import { mocked, type MockedObject } from "jest-mock";
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import StorageEvictedDialog from "../../src/components/views/dialogs/StorageEvictedDialog";
import * as Lifecycle from "../../src/Lifecycle";
@ -23,7 +23,6 @@ import { idbSave } from "../../src/utils/StorageAccess";
import { flushPromises, getMockClientWithEventEmitter, mockClientMethodsUser, mockPlatformPeg } from "../test-utils";
import { OidcClientStore } from "../../src/stores/oidc/OidcClientStore";
import { makeDelegatedAuthConfig } from "../test-utils/oidc";
import { persistOidcAuthenticatedSettings } from "../../src/utils/oidc/persistOidcSettings";
import { Action } from "../../src/dispatcher/actions";
import PlatformPeg from "../../src/PlatformPeg";
import { persistAccessTokenInStorage, persistRefreshTokenInStorage } from "../../src/utils/tokens/tokens";
@ -95,43 +94,6 @@ describe("Lifecycle", () => {
window.crypto = windowCrypto;
});
const initLocalStorageMock = (mockStore: Record<string, unknown> = {}): void => {
jest.spyOn(localStorage.__proto__, "getItem")
.mockClear()
.mockImplementation((key: unknown) => mockStore[key as string] ?? null);
jest.spyOn(localStorage.__proto__, "removeItem")
.mockClear()
.mockImplementation((key: unknown) => {
const { [key as string]: toRemove, ...newStore } = mockStore;
mockStore = newStore;
return toRemove;
});
jest.spyOn(localStorage.__proto__, "setItem")
.mockClear()
.mockImplementation((key: unknown, value: unknown) => {
mockStore[key as string] = value;
});
};
const initSessionStorageMock = (mockStore: Record<string, unknown> = {}): void => {
jest.spyOn(sessionStorage.__proto__, "getItem")
.mockClear()
.mockImplementation((key: unknown) => mockStore[key as string] ?? null);
jest.spyOn(sessionStorage.__proto__, "removeItem")
.mockClear()
.mockImplementation((key: unknown) => {
const { [key as string]: toRemove, ...newStore } = mockStore;
mockStore = newStore;
return toRemove;
});
jest.spyOn(sessionStorage.__proto__, "setItem")
.mockClear()
.mockImplementation((key: unknown, value: unknown) => {
mockStore[key as string] = value;
});
jest.spyOn(sessionStorage.__proto__, "clear").mockClear();
};
const initIdbMock = (mockStore: Record<string, Record<string, unknown>> = {}): void => {
jest.spyOn(StorageAccess, "idbLoad")
.mockClear()
@ -162,7 +124,7 @@ describe("Lifecycle", () => {
});
};
const localStorageSession = {
const localStorageSession: Record<string, string> = {
mx_hs_url: homeserverUrl,
mx_is_url: identityServerUrl,
mx_user_id: userId,
@ -219,8 +181,6 @@ describe("Lifecycle", () => {
describe("restoreSessionFromStorage()", () => {
beforeEach(() => {
initLocalStorageMock();
initSessionStorageMock();
initIdbMock();
jest.clearAllMocks();
@ -250,7 +210,7 @@ describe("Lifecycle", () => {
});
it("should abort login when we expect to find an access token but don't", async () => {
initLocalStorageMock({ mx_has_access_token: "true" });
localStorage.setItem("mx_has_access_token", "true");
await expect(() => restoreSessionFromStorage()).rejects.toThrow();
expect(Modal.createDialog).toHaveBeenCalledWith(StorageEvictedDialog);
@ -260,7 +220,10 @@ describe("Lifecycle", () => {
describe("when session is found in storage", () => {
describe("guest account", () => {
beforeEach(() => {
initLocalStorageMock({ ...localStorageSession, mx_is_guest: "true" });
localStorage.setItem("mx_is_guest", "true");
for (const key in localStorageSession) {
localStorage.setItem(key, localStorageSession[key]);
}
initIdbMock(idbStorageSession);
});
@ -279,27 +242,29 @@ describe("Lifecycle", () => {
}),
undefined,
);
expect(localStorage.setItem).toHaveBeenCalledWith("mx_is_guest", "true");
expect(localStorage.getItem("mx_is_guest")).toEqual("true");
});
});
describe("without a pickle key", () => {
beforeEach(() => {
initLocalStorageMock(localStorageSession);
for (const key in localStorageSession) {
localStorage.setItem(key, localStorageSession[key]);
}
initIdbMock(idbStorageSession);
});
it("should persist credentials", async () => {
expect(await restoreSessionFromStorage()).toEqual(true);
expect(localStorage.setItem).toHaveBeenCalledWith("mx_user_id", userId);
expect(localStorage.setItem).toHaveBeenCalledWith("mx_has_access_token", "true");
expect(localStorage.setItem).toHaveBeenCalledWith("mx_is_guest", "false");
expect(localStorage.setItem).toHaveBeenCalledWith("mx_device_id", deviceId);
expect(localStorage.getItem("mx_user_id")).toEqual(userId);
expect(localStorage.getItem("mx_has_access_token")).toEqual("true");
expect(localStorage.getItem("mx_is_guest")).toEqual("false");
expect(localStorage.getItem("mx_device_id")).toEqual(deviceId);
expect(StorageAccess.idbSave).toHaveBeenCalledWith("account", "mx_access_token", accessToken);
// dont put accessToken in localstorage when we have idb
expect(localStorage.setItem).not.toHaveBeenCalledWith("mx_access_token", accessToken);
expect(localStorage.getItem("mx_access_token")).not.toEqual(accessToken);
});
it("should persist access token when idb is not available", async () => {
@ -308,7 +273,7 @@ describe("Lifecycle", () => {
expect(StorageAccess.idbSave).toHaveBeenCalledWith("account", "mx_access_token", accessToken);
// put accessToken in localstorage as fallback
expect(localStorage.setItem).toHaveBeenCalledWith("mx_access_token", accessToken);
expect(localStorage.getItem("mx_access_token")).toEqual(accessToken);
});
it("should create and start new matrix client with credentials", async () => {
@ -334,7 +299,7 @@ describe("Lifecycle", () => {
it("should remove fresh login flag from session storage", async () => {
expect(await restoreSessionFromStorage()).toEqual(true);
expect(sessionStorage.removeItem).toHaveBeenCalledWith("mx_fresh_login");
expect(sessionStorage.getItem("mx_fresh_login")).toBeFalsy();
});
it("should start matrix client", async () => {
@ -345,10 +310,10 @@ describe("Lifecycle", () => {
describe("with a refresh token", () => {
beforeEach(() => {
initLocalStorageMock({
...localStorageSession,
mx_refresh_token: refreshToken,
});
localStorage.setItem("mx_refresh_token", refreshToken);
for (const key in localStorageSession) {
localStorage.setItem(key, localStorageSession[key]);
}
initIdbMock(idbStorageSession);
});
@ -356,7 +321,7 @@ describe("Lifecycle", () => {
expect(await restoreSessionFromStorage()).toEqual(true);
// refresh token from storage is re-persisted
expect(localStorage.setItem).toHaveBeenCalledWith("mx_has_refresh_token", "true");
expect(localStorage.getItem("mx_has_refresh_token")).toEqual("true");
expect(StorageAccess.idbSave).toHaveBeenCalledWith("account", "mx_refresh_token", refreshToken);
});
@ -386,7 +351,9 @@ describe("Lifecycle", () => {
let pickleKey: string;
beforeEach(async () => {
initLocalStorageMock(localStorageSession);
for (const key in localStorageSession) {
localStorage.setItem(key, localStorageSession[key]);
}
initIdbMock({});
// Create a pickle key, and store it, encrypted, in IDB.
@ -401,7 +368,7 @@ describe("Lifecycle", () => {
it("should persist credentials", async () => {
expect(await restoreSessionFromStorage()).toEqual(true);
expect(localStorage.setItem).toHaveBeenCalledWith("mx_has_access_token", "true");
expect(localStorage.getItem("mx_has_access_token")).toEqual("true");
// token encrypted and persisted
expect(StorageAccess.idbSave).toHaveBeenCalledWith(
@ -429,7 +396,7 @@ describe("Lifecycle", () => {
encryptedTokenShapedObject,
);
// put accessToken in localstorage as fallback
expect(localStorage.setItem).toHaveBeenCalledWith("mx_access_token", accessToken);
expect(localStorage.getItem("mx_access_token")).toEqual(accessToken);
});
it("should create and start new matrix client with credentials", async () => {
@ -470,7 +437,7 @@ describe("Lifecycle", () => {
expect(await restoreSessionFromStorage()).toEqual(true);
// refresh token from storage is re-persisted
expect(localStorage.setItem).toHaveBeenCalledWith("mx_has_refresh_token", "true");
expect(localStorage.getItem("mx_has_refresh_token")).toEqual("true");
expect(StorageAccess.idbSave).toHaveBeenCalledWith(
"account",
"mx_refresh_token",
@ -505,7 +472,9 @@ describe("Lifecycle", () => {
let pickleKey: string;
beforeEach(async () => {
initLocalStorageMock(localStorageSession);
for (const key in localStorageSession) {
localStorage.setItem(key, localStorageSession[key]);
}
initIdbMock({});
// Generate the pickle key. I don't *think* it's possible for there to be a pickle key
@ -552,7 +521,9 @@ describe("Lifecycle", () => {
});
it("should proceed if server is not accessible", async () => {
initLocalStorageMock(localStorageSession);
for (const key in localStorageSession) {
localStorage.setItem(key, localStorageSession[key]);
}
initIdbMock(idbStorageSession);
mockClient.isVersionSupported.mockRejectedValue(new Error("Oh, noes, the server is down!"));
@ -560,7 +531,9 @@ describe("Lifecycle", () => {
});
it("should throw if the token was persisted with a pickle key but there is no pickle key available now", async () => {
initLocalStorageMock(localStorageSession);
for (const key in localStorageSession) {
localStorage.setItem(key, localStorageSession[key]);
}
initIdbMock({});
// Create a pickle key, and store it, encrypted, in IDB.
@ -580,8 +553,6 @@ describe("Lifecycle", () => {
describe("setLoggedIn()", () => {
beforeEach(() => {
initLocalStorageMock();
initSessionStorageMock();
initIdbMock();
jest.clearAllMocks();
@ -599,7 +570,7 @@ describe("Lifecycle", () => {
it("should remove fresh login flag from session storage", async () => {
await setLoggedIn(credentials);
expect(sessionStorage.removeItem).toHaveBeenCalledWith("mx_fresh_login");
expect(sessionStorage.getItem("mx_fresh_login")).toBeFalsy();
});
it("should start matrix client", async () => {
@ -617,7 +588,7 @@ describe("Lifecycle", () => {
it("should not clear the storage if device is the same", async () => {
await Lifecycle.hydrateSession(credentials);
expect(localStorage.removeItem).toHaveBeenCalledWith("mx_soft_logout");
expect(localStorage.getItem("mx_soft_logout")).toBeFalsy();
expect(mockClient.getUserId).toHaveReturnedWith(userId);
expect(mockClient.getDeviceId).toHaveReturnedWith(deviceId);
expect(mockClient.clearStores).toHaveBeenCalledTimes(1);
@ -633,7 +604,7 @@ describe("Lifecycle", () => {
};
await Lifecycle.hydrateSession(fakeCredentials);
expect(localStorage.removeItem).toHaveBeenCalledWith("mx_soft_logout");
expect(localStorage.getItem("mx_soft_logout")).toBeFalsy();
expect(mockClient.getUserId).toHaveReturnedWith(userId);
expect(mockClient.getDeviceId).toHaveReturnedWith(deviceId);
expect(mockClient.clearStores).toHaveBeenCalledTimes(2);
@ -648,21 +619,19 @@ describe("Lifecycle", () => {
it("should persist credentials", async () => {
await setLoggedIn(credentials);
expect(localStorage.setItem).toHaveBeenCalledWith("mx_user_id", userId);
expect(localStorage.setItem).toHaveBeenCalledWith("mx_has_access_token", "true");
expect(localStorage.setItem).toHaveBeenCalledWith("mx_is_guest", "false");
expect(localStorage.setItem).toHaveBeenCalledWith("mx_device_id", deviceId);
expect(localStorage.getItem("mx_user_id")).toEqual(userId);
expect(localStorage.getItem("mx_has_access_token")).toEqual("true");
expect(localStorage.getItem("mx_is_guest")).toEqual("false");
expect(localStorage.getItem("mx_device_id")).toEqual(deviceId);
expect(StorageAccess.idbSave).toHaveBeenCalledWith("account", "mx_access_token", accessToken);
// dont put accessToken in localstorage when we have idb
expect(localStorage.setItem).not.toHaveBeenCalledWith("mx_access_token", accessToken);
expect(localStorage.getItem("mx_access_token")).not.toEqual(accessToken);
});
it("should persist a refreshToken when present", async () => {
initLocalStorageMock({
mx_oidc_token_issuer: "test-issuer.dummy",
mx_oidc_client_id: "test-client-id",
});
localStorage.setItem("mx_oidc_token_issuer", "test-issuer.dummy");
localStorage.setItem("mx_oidc_client_id", "test-client-id");
await setLoggedIn({
...credentials,
@ -672,7 +641,7 @@ describe("Lifecycle", () => {
expect(StorageAccess.idbSave).toHaveBeenCalledWith("account", "mx_access_token", accessToken);
expect(StorageAccess.idbSave).toHaveBeenCalledWith("account", "mx_refresh_token", refreshToken);
// dont put accessToken in localstorage when we have idb
expect(localStorage.setItem).not.toHaveBeenCalledWith("mx_access_token", accessToken);
expect(localStorage.getItem("mx_access_token")).not.toEqual(accessToken);
});
it("should remove any access token from storage when there is none in credentials and idb save fails", async () => {
@ -683,15 +652,15 @@ describe("Lifecycle", () => {
accessToken: undefined,
});
expect(localStorage.removeItem).toHaveBeenCalledWith("mx_has_access_token");
expect(localStorage.removeItem).toHaveBeenCalledWith("mx_access_token");
expect(localStorage.getItem("mx_has_access_token")).toBeFalsy();
expect(localStorage.getItem("mx_access_token")).toBeFalsy();
});
it("should clear stores", async () => {
await setLoggedIn(credentials);
expect(StorageAccess.idbClear).toHaveBeenCalledWith("account");
expect(sessionStorage.clear).toHaveBeenCalled();
expect(sessionStorage.length).toBe(0);
expect(mockClient.clearStores).toHaveBeenCalled();
});
@ -735,12 +704,12 @@ describe("Lifecycle", () => {
it("should persist credentials", async () => {
await setLoggedIn(credentials);
expect(localStorage.setItem).toHaveBeenCalledWith("mx_user_id", userId);
expect(localStorage.setItem).toHaveBeenCalledWith("mx_has_access_token", "true");
expect(localStorage.setItem).toHaveBeenCalledWith("mx_is_guest", "false");
expect(localStorage.setItem).toHaveBeenCalledWith("mx_device_id", deviceId);
expect(localStorage.getItem("mx_user_id")).toEqual(userId);
expect(localStorage.getItem("mx_has_access_token")).toEqual("true");
expect(localStorage.getItem("mx_is_guest")).toEqual("false");
expect(localStorage.getItem("mx_device_id")).toEqual(deviceId);
expect(localStorage.setItem).toHaveBeenCalledWith("mx_has_pickle_key", "true");
expect(localStorage.getItem("mx_has_pickle_key")).toEqual("true");
expect(StorageAccess.idbSave).toHaveBeenCalledWith(
"account",
"mx_access_token",
@ -748,7 +717,7 @@ describe("Lifecycle", () => {
);
expect(StorageAccess.idbSave).toHaveBeenCalledWith("pickleKey", [userId, deviceId], expect.any(Object));
// dont put accessToken in localstorage when we have idb
expect(localStorage.setItem).not.toHaveBeenCalledWith("mx_access_token", accessToken);
expect(localStorage.getItem("mx_access_token")).not.toEqual(accessToken);
});
it("should persist token when encrypting the token fails", async () => {
@ -771,7 +740,7 @@ describe("Lifecycle", () => {
await setLoggedIn(credentials);
// put plain accessToken in localstorage when we dont have idb
expect(localStorage.setItem).toHaveBeenCalledWith("mx_access_token", accessToken);
expect(localStorage.getItem("mx_access_token")).toEqual(accessToken);
});
it("should remove any access token from storage when there is none in credentials and idb save fails", async () => {
@ -789,8 +758,8 @@ describe("Lifecycle", () => {
accessToken: undefined,
});
expect(localStorage.removeItem).toHaveBeenCalledWith("mx_has_access_token");
expect(localStorage.removeItem).toHaveBeenCalledWith("mx_access_token");
expect(localStorage.getItem("mx_has_access_token")).toBeFalsy();
expect(localStorage.getItem("mx_access_token")).toBeFalsy();
});
it("should create new matrix client with credentials", async () => {
@ -812,7 +781,8 @@ describe("Lifecycle", () => {
});
});
describe("when authenticated via OIDC native flow", () => {
// XXX: these tests are broken, Lifecycle.setLoggedIn does not work with OIDC and its token refreshers due to clearing storage
describe.skip("when authenticated via OIDC native flow", () => {
const clientId = "test-client-id";
const issuer = "https://auth.com/";
@ -820,7 +790,7 @@ describe("Lifecycle", () => {
const idToken =
"eyJhbGciOiJSUzI1NiIsImtpZCI6Imh4ZEhXb0Y5bW4ifQ.eyJzdWIiOiIwMUhQUDJGU0JZREU5UDlFTU04REQ3V1pIUiIsImlzcyI6Imh0dHBzOi8vYXV0aC1vaWRjLmxhYi5lbGVtZW50LmRldi8iLCJpYXQiOjE3MTUwNzE5ODUsImF1dGhfdGltZSI6MTcwNzk5MDMxMiwiY19oYXNoIjoidGt5R1RhUjU5aTk3YXoyTU4yMGdidyIsImV4cCI6MTcxNTA3NTU4NSwibm9uY2UiOiJxaXhwM0hFMmVaIiwiYXVkIjoiMDFIWDk0Mlg3QTg3REgxRUs2UDRaNjI4WEciLCJhdF9oYXNoIjoiNFlFUjdPRlVKTmRTeEVHV2hJUDlnZyJ9.HxODneXvSTfWB5Vc4cf7b8GiN2gdwUuTiyVqZuupWske2HkZiJZUt5Lsxg9BW3gz28POkE0Ln17snlkmy02B_AD3DQxKOOxQCzIIARHdfFvZxgGWsMdFcVQZDW7rtXcqgj-SpVaUQ_8acsgxSrz_DF2o0O4tto0PT6wVUiw8KlBmgWTscWPeAWe-39T-8EiQ8Wi16h6oSPcz2NzOQ7eOM_S9fDkOorgcBkRGLl1nrahrPSdWJSGAeruk5mX4YxN714YThFDyEA2t9YmKpjaiSQ2tT-Xkd7tgsZqeirNs2ni9mIiFX3bRX6t2AhUNzA7MaX9ZyizKGa6go3BESO_oDg";
beforeAll(() => {
beforeEach(() => {
fetchMock.get(`${delegatedAuthConfig.issuer}.well-known/openid-configuration`, delegatedAuthConfig);
fetchMock.get(`${delegatedAuthConfig.issuer}jwks`, {
status: 200,
@ -829,27 +799,24 @@ describe("Lifecycle", () => {
},
keys: [],
});
});
beforeEach(() => {
initSessionStorageMock();
// set values in session storage as they would be after a successful oidc authentication
persistOidcAuthenticatedSettings(clientId, issuer, idToken);
// set values in local storage as they would be after a successful oidc authentication
localStorage.setItem("mx_oidc_client_id", clientId);
localStorage.setItem("mx_oidc_token_issuer", issuer);
localStorage.setItem("mx_oidc_id_token", idToken);
});
it("should not try to create a token refresher without a refresh token", async () => {
await setLoggedIn(credentials);
// didn't try to initialise token refresher
expect(fetchMock).not.toHaveFetched(`${delegatedAuthConfig.issuer}.well-known/openid-configuration`);
expect(fetchMock).toHaveFetchedTimes(
0,
`${delegatedAuthConfig.issuer}.well-known/openid-configuration`,
);
});
it("should not try to create a token refresher without a deviceId", async () => {
initLocalStorageMock({
mx_oidc_token_issuer: "test-issuer.dummy",
mx_oidc_client_id: "test-client-id",
});
await expect(
setLoggedIn({
...credentials,
@ -859,16 +826,14 @@ describe("Lifecycle", () => {
).rejects.toThrow("Expected deviceId in user credentials.");
// didn't try to initialise token refresher
expect(fetchMock).not.toHaveFetched(`${delegatedAuthConfig.issuer}.well-known/openid-configuration`);
expect(fetchMock).toHaveFetchedTimes(
0,
`${delegatedAuthConfig.issuer}.well-known/openid-configuration`,
);
});
it("should not try to create a token refresher without an issuer in session storage", async () => {
persistOidcAuthenticatedSettings(
clientId,
// @ts-ignore set undefined issuer
undefined,
idToken,
);
localStorage.removeItem("mx_oidc_token_issuer");
await expect(
setLoggedIn({
...credentials,
@ -877,7 +842,10 @@ describe("Lifecycle", () => {
).rejects.toThrow("Cannot create an OIDC token refresher as no stored OIDC token issuer was found.");
// didn't try to initialise token refresher
expect(fetchMock).not.toHaveFetched(`${delegatedAuthConfig.issuer}.well-known/openid-configuration`);
expect(fetchMock).toHaveFetchedTimes(
0,
`${delegatedAuthConfig.issuer}.well-known/openid-configuration`,
);
});
it("should create a client with a tokenRefreshFunction", async () => {
@ -898,8 +866,8 @@ describe("Lifecycle", () => {
});
it("should create a client when creating token refresher fails", async () => {
// set invalid value in session storage for a malformed oidc authentication
persistOidcAuthenticatedSettings(null as any, issuer, idToken);
// create invalid value in local storage for a malformed oidc authentication
localStorage.removeItem("mx_oidc_client_id");
// succeeded
expect(

View File

@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
*/
import { logger } from "matrix-js-sdk/src/logger";
import fetchMockJest from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import { advanceDateAndTime, stubClient } from "../test-utils";
import { type IMatrixClientPeg, MatrixClientPeg as peg } from "../../src/MatrixClientPeg";
@ -69,7 +69,7 @@ describe("MatrixClientPeg", () => {
beforeEach(() => {
// instantiate a MatrixClientPegClass instance, with a new MatrixClient
testPeg = new PegClass();
fetchMockJest.get("http://example.com/_matrix/client/versions", {});
fetchMock.get("http://example.com/_matrix/client/versions", {});
testPeg.replaceUsingCreds({
accessToken: "SEKRET",
homeserverUrl: "http://example.com",

View File

@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
*/
import { mocked } from "jest-mock";
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import { type MatrixClient } from "matrix-js-sdk/src/matrix";
import ScalarAuthClient from "../../src/ScalarAuthClient";

View File

@ -9,7 +9,7 @@ Please see LICENSE files in the repository root for full details.
import { type SlidingSync, SlidingSyncEvent, SlidingSyncState } from "matrix-js-sdk/src/sliding-sync";
import { mocked } from "jest-mock";
import { ClientEvent, type MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
import fetchMockJest from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import EventEmitter from "events";
import { waitFor } from "jest-matrix-react";
@ -45,8 +45,7 @@ describe("SlidingSyncManager", () => {
mocked(client.getRoom).mockReturnValue(null);
(manager as any).configure(client, "invalid");
manager.slidingSync = slidingSync;
fetchMockJest.reset();
fetchMockJest.get("https://proxy/client/server.json", {});
fetchMock.get("https://proxy/client/server.json", {});
});
describe("setRoomVisible", () => {

View File

@ -11,13 +11,8 @@ import { render } from "jest-matrix-react";
import SdkConfig from "../../../../src/SdkConfig";
import { ErrorView, UnsupportedBrowserView } from "../../../../src/async-components/structures/ErrorView";
import { setupLanguageMock } from "../../../setup/setupLanguage";
describe("<ErrorView />", () => {
beforeEach(() => {
setupLanguageMock();
});
it("should match snapshot", () => {
const { asFragment } = render(<ErrorView title="TITLE" messages={["MSG1", "MSG2"]} />);
expect(asFragment()).toMatchSnapshot();
@ -26,7 +21,6 @@ describe("<ErrorView />", () => {
describe("<UnsupportedBrowserView />", () => {
beforeEach(() => {
setupLanguageMock();
SdkConfig.put({});
});

View File

@ -6,13 +6,10 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
// fake-indexeddb needs this and the tests crash without it
// https://github.com/dumbmatter/fakeIndexedDB?tab=readme-ov-file#jsdom-often-used-with-jest
import "core-js/stable/structured-clone";
import "fake-indexeddb/auto";
import React, { type ComponentProps } from "react";
import { fireEvent, render, type RenderResult, screen, waitFor, within, act } from "jest-matrix-react";
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import { type Mocked, mocked } from "jest-mock";
import { ClientEvent, type MatrixClient, MatrixEvent, Room, SyncState } from "matrix-js-sdk/src/matrix";
import { type MediaHandler } from "matrix-js-sdk/src/webrtc/mediaHandler";
@ -1629,7 +1626,7 @@ describe("<MatrixChat />", () => {
// Flaky test, see https://github.com/element-hq/element-web/issues/30337
it("waits for other tab to stop during startup", async () => {
fetchMock.get("/welcome.html", { body: "<h1>Hello</h1>" });
fetchMock.get("end:/welcome.html", { body: "<h1>Hello</h1>" });
jest.spyOn(Lifecycle, "attemptDelegatedAuthLogin");
// simulate an active window

View File

@ -11,7 +11,7 @@ import { render, screen, waitFor } from "jest-matrix-react";
import { DEVICE_CODE_SCOPE, type MatrixClient, type Room } from "matrix-js-sdk/src/matrix";
import { type CryptoApi } from "matrix-js-sdk/src/crypto-api";
import { mocked } from "jest-mock";
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import UnwrappedUserMenu from "../../../../src/components/structures/UserMenu";
import { stubClient, wrapInSdkContext } from "../../../test-utils";

View File

@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details.
import React from "react";
import { fireEvent, render, screen, waitForElementToBeRemoved } from "jest-matrix-react";
import { mocked, type MockedObject } from "jest-mock";
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import { DELEGATED_OIDC_COMPATIBILITY, IdentityProviderBrand, type OidcClientConfig } from "matrix-js-sdk/src/matrix";
import { logger } from "matrix-js-sdk/src/logger";
import * as Matrix from "matrix-js-sdk/src/matrix";
@ -18,7 +18,6 @@ import SdkConfig from "../../../../../src/SdkConfig";
import { mkServerConfig, mockPlatformPeg, unmockPlatformPeg } from "../../../../test-utils";
import Login from "../../../../../src/components/structures/auth/Login";
import type BasePlatform from "../../../../../src/BasePlatform";
import SettingsStore from "../../../../../src/settings/SettingsStore";
import * as registerClientUtils from "../../../../../src/utils/oidc/registerClient";
import { makeDelegatedAuthConfig } from "../../../../test-utils/oidc";
@ -55,8 +54,6 @@ describe("Login", function () {
mockClient.baseUrl = opts.baseUrl;
return mockClient;
});
fetchMock.resetBehavior();
fetchMock.resetHistory();
fetchMock.get("https://matrix.org/_matrix/client/versions", {
unstable_features: {},
versions: ["v1.1"],
@ -67,7 +64,6 @@ describe("Login", function () {
});
afterEach(function () {
fetchMock.restore();
SdkConfig.reset(); // we touch the config, so clean up
unmockPlatformPeg();
});
@ -327,7 +323,7 @@ describe("Login", function () {
});
it("should display an error when homeserver fails liveliness check", async () => {
fetchMock.resetBehavior();
fetchMock.removeRoutes();
fetchMock.get("https://matrix.org/_matrix/client/versions", {
status: 0,
});
@ -339,7 +335,7 @@ describe("Login", function () {
});
it("should reset liveliness error when server config changes", async () => {
fetchMock.resetBehavior();
fetchMock.removeRoutes();
// matrix.org is not alive
fetchMock.get("https://matrix.org/_matrix/client/versions", {
status: 400,
@ -376,21 +372,6 @@ describe("Login", function () {
jest.spyOn(logger, "error").mockRestore();
});
it("should not attempt registration when oidc native flow setting is disabled", async () => {
jest.spyOn(SettingsStore, "getValue").mockReturnValue(false);
getComponent(hsUrl, isUrl, delegatedAuth);
await waitForElementToBeRemoved(() => screen.queryAllByLabelText("Loading…"));
// didn't try to register
expect(fetchMock).not.toHaveBeenCalledWith(delegatedAuth.registration_endpoint);
// continued with normal setup
expect(mockClient.loginFlows).toHaveBeenCalled();
// normal password login rendered
expect(screen.getByLabelText("Username")).toBeInTheDocument();
});
it("should attempt to register oidc client", async () => {
// dont mock, spy so we can check config values were correctly passed
jest.spyOn(registerClientUtils, "getOidcClientId");
@ -400,7 +381,7 @@ describe("Login", function () {
await waitForElementToBeRemoved(() => screen.queryAllByLabelText("Loading…"));
// tried to register
expect(fetchMock).toHaveBeenCalledWith(delegatedAuth.registration_endpoint, expect.any(Object));
expect(fetchMock).toHaveFetched(delegatedAuth.registration_endpoint);
// called with values from config
expect(registerClientUtils.getOidcClientId).toHaveBeenCalledWith(delegatedAuth, oidcStaticClientsConfig);
});
@ -412,7 +393,7 @@ describe("Login", function () {
await waitForElementToBeRemoved(() => screen.queryAllByLabelText("Loading…"));
// tried to register
expect(fetchMock).toHaveBeenCalledWith(delegatedAuth.registration_endpoint, expect.any(Object));
expect(fetchMock).toHaveFetched(delegatedAuth.registration_endpoint);
expect(logger.error).toHaveBeenCalledWith(new Error(OidcError.DynamicRegistrationFailed));
// continued with normal setup
@ -432,38 +413,5 @@ describe("Login", function () {
expect(mockClient.loginFlows).not.toHaveBeenCalled();
expect(screen.getByText("Continue")).toBeInTheDocument();
});
/**
* Oidc-aware flows still work while the oidc-native feature flag is disabled
*/
it("should show oidc-aware flow for oidc-enabled homeserver when oidc native flow setting is disabled", async () => {
jest.spyOn(SettingsStore, "getValue").mockReturnValue(false);
mockClient.loginFlows.mockResolvedValue({
flows: [
{
type: "m.login.sso",
[DELEGATED_OIDC_COMPATIBILITY.name]: true,
},
{
type: "m.login.password",
},
],
});
const { container } = getComponent(hsUrl, isUrl, delegatedAuth);
await waitForElementToBeRemoved(() => screen.queryAllByLabelText("Loading…"));
// didn't try to register
expect(fetchMock).not.toHaveBeenCalledWith(delegatedAuth.registration_endpoint);
// continued with normal setup
expect(mockClient.loginFlows).toHaveBeenCalled();
// oidc-aware 'continue' button displayed
const ssoButtons = container.querySelectorAll(".mx_SSOButton");
expect(ssoButtons.length).toBe(1);
expect(ssoButtons[0].textContent).toBe("Continue");
// no password form visible
expect(container.querySelector("form")).toBeFalsy();
});
});
});

View File

@ -11,7 +11,7 @@ import React from "react";
import { fireEvent, render, screen, waitFor, waitForElementToBeRemoved } from "jest-matrix-react";
import { createClient, type MatrixClient, MatrixError, type OidcClientConfig } from "matrix-js-sdk/src/matrix";
import { mocked, type MockedObject } from "jest-mock";
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import SdkConfig, { DEFAULTS } from "../../../../../src/SdkConfig";
import {
@ -75,7 +75,6 @@ describe("Registration", function () {
afterEach(function () {
jest.restoreAllMocks();
fetchMock.restore();
SdkConfig.reset(); // we touch the config, so clean up
unmockPlatformPeg();
});

View File

@ -10,13 +10,8 @@ import React from "react";
import { render } from "jest-matrix-react";
import AuthFooter from "../../../../../src/components/views/auth/AuthFooter";
import { setupLanguageMock } from "../../../../setup/setupLanguage";
describe("<AuthFooter />", () => {
beforeEach(() => {
setupLanguageMock();
});
it("should match snapshot", () => {
const { asFragment } = render(<AuthFooter />);
expect(asFragment()).toMatchSnapshot();

View File

@ -10,12 +10,10 @@ import React from "react";
import { render } from "jest-matrix-react";
import AuthPage from "../../../../../src/components/views/auth/AuthPage";
import { setupLanguageMock } from "../../../../setup/setupLanguage";
import SdkConfig from "../../../../../src/SdkConfig.ts";
describe("<AuthPage />", () => {
beforeEach(() => {
setupLanguageMock();
SdkConfig.reset();
// @ts-ignore private access
AuthPage.welcomeBackgroundUrl = undefined;

View File

@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
*/
import React from "react";
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import { render, screen } from "jest-matrix-react";
import { mocked } from "jest-mock";
@ -19,7 +19,7 @@ jest.mock("../../../../../src/languageHandler", () => ({
}));
describe("<EmbeddedPage />", () => {
it.each([`"`, `'`, `&#x27;`, `&#x34;`])("should translate _t strings", async (character) => {
it.each([`"`, `'`, `&#27;`, `&#34;`])("should translate _t strings [%s]", async (character) => {
mocked(_t).mockReturnValue("Przeglądaj pokoje");
fetchMock.get("https://home.page", {
body: `<h1>_t(${character}Explore rooms${character})</h1>`,

View File

@ -665,7 +665,10 @@ describe("MessageContextMenu", () => {
it("shows view in room button when the event is a thread root", () => {
const eventContent = createMessageEventContent("hello");
const mxEvent = new MatrixEvent({ type: EventType.RoomMessage, content: eventContent });
mxEvent.getThread = () => ({ rootEvent: mxEvent }) as Thread;
mxEvent.getThread = () =>
({
rootEvent: mxEvent,
}) as Thread;
const props = {
rightClick: true,
};

View File

@ -26,7 +26,7 @@ exports[`<EmbeddedPage /> should show error if unable to load 1`] = `
</DocumentFragment>
`;
exports[`<EmbeddedPage /> should translate _t strings 1`] = `
exports[`<EmbeddedPage /> should translate _t strings ["] 1`] = `
<DocumentFragment>
<div
class="undefined_guest"
@ -42,7 +42,7 @@ exports[`<EmbeddedPage /> should translate _t strings 1`] = `
</DocumentFragment>
`;
exports[`<EmbeddedPage /> should translate _t strings 2`] = `
exports[`<EmbeddedPage /> should translate _t strings [&#27;] 1`] = `
<DocumentFragment>
<div
class="undefined_guest"
@ -58,7 +58,7 @@ exports[`<EmbeddedPage /> should translate _t strings 2`] = `
</DocumentFragment>
`;
exports[`<EmbeddedPage /> should translate _t strings 3`] = `
exports[`<EmbeddedPage /> should translate _t strings [&#34;] 1`] = `
<DocumentFragment>
<div
class="undefined_guest"
@ -74,7 +74,7 @@ exports[`<EmbeddedPage /> should translate _t strings 3`] = `
</DocumentFragment>
`;
exports[`<EmbeddedPage /> should translate _t strings 4`] = `
exports[`<EmbeddedPage /> should translate _t strings ['] 1`] = `
<DocumentFragment>
<div
class="undefined_guest"

View File

@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details.
import { render, waitFor, type RenderResult } from "jest-matrix-react";
import userEvent from "@testing-library/user-event";
import React from "react";
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import { type Mocked } from "jest-mock";
import BugReportDialog, {
@ -59,7 +59,6 @@ describe("BugReportDialog", () => {
global.mx_rage_logger = prevLogger;
jest.restoreAllMocks();
SdkConfig.reset();
fetchMock.restore();
});
it("can close the bug reporter", async () => {

View File

@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
*/
import React from "react";
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import { render, screen, waitForElementToBeRemoved } from "jest-matrix-react";
import ChangelogDialog, {

View File

@ -56,8 +56,7 @@ describe("DevtoolsDialog", () => {
const copiedBtn = getByLabelText(container, "Copied!");
expect(copiedBtn).toBeInTheDocument();
expect(navigator.clipboard.writeText).toHaveBeenCalled();
await expect(navigator.clipboard.readText()).resolves.toBe(room.roomId);
expect(navigator.clipboard.writeText).toHaveBeenCalledWith(room.roomId);
});
it("copies the thread root id when provided", async () => {
@ -72,7 +71,6 @@ describe("DevtoolsDialog", () => {
const copiedBtn = getByLabelText(container, "Copied!");
expect(copiedBtn).toBeInTheDocument();
expect(navigator.clipboard.writeText).toHaveBeenCalled();
await expect(navigator.clipboard.readText()).resolves.toBe(threadRootId);
expect(navigator.clipboard.writeText).toHaveBeenCalledWith(threadRootId);
});
});

View File

@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details.
import React from "react";
import { fireEvent, render, screen } from "jest-matrix-react";
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import ServerPickerDialog from "../../../../../src/components/views/dialogs/ServerPickerDialog";
import SdkConfig from "../../../../../src/SdkConfig";
@ -54,7 +54,7 @@ describe("<ServerPickerDialog />", () => {
validated_server_config: defaultServerConfig,
});
fetchMock.resetHistory();
fetchMock.clearHistory();
fetchMock.catch({
status: 404,
body: '{"errcode": "M_UNRECOGNIZED", "error": "Unrecognized request"}',

View File

@ -9,7 +9,7 @@
import React from "react";
import { mocked } from "jest-mock";
import { render, fireEvent, waitFor } from "jest-matrix-react";
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
import ImageView from "../../../../../src/components/views/elements/ImageView";
@ -23,7 +23,6 @@ jest.mock("../../../../../src/utils/FileDownloader");
describe("<ImageView />", () => {
beforeEach(() => {
jest.resetAllMocks();
fetchMock.reset();
});
it("renders correctly", () => {

View File

@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details.
import React from "react";
import { mocked } from "jest-mock";
import fetchMockJest from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import { fireEvent, render, screen, waitFor } from "jest-matrix-react";
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
import userEvent from "@testing-library/user-event";
@ -37,7 +37,6 @@ describe("DownloadActionButton", () => {
beforeEach(() => {
jest.restoreAllMocks();
fetchMockJest.restore();
});
afterEach(() => {
@ -51,7 +50,7 @@ describe("DownloadActionButton", () => {
(mxc) => `https://matrix.org/_matrix/media/r0/download/${mxc.slice(6)}`,
);
fetchMockJest.getOnce("https://matrix.org/_matrix/media/r0/download/matrix.org/1234", {
fetchMock.getOnce("https://matrix.org/_matrix/media/r0/download/matrix.org/1234", {
status: 404,
body: { errcode: "M_NOT_FOUND", error: "Not found" },
});
@ -78,7 +77,7 @@ describe("DownloadActionButton", () => {
const user = userEvent.setup();
fetchMockJest.getOnce("https://matrix.org/_matrix/media/r0/download/matrix.org/1234", "TESTFILE");
fetchMock.getOnce("https://matrix.org/_matrix/media/r0/download/matrix.org/1234", "TESTFILE");
const event = new MatrixEvent({
room_id: "!room:id",
@ -106,7 +105,7 @@ describe("DownloadActionButton", () => {
stubClient();
fetchMockJest.getOnce("http://this.is.a.url/matrix.org/1234", "TESTFILE");
fetchMock.getOnce("http://this.is.a.url/matrix.org/1234", "TESTFILE");
const mediaEventHelper = new MediaEventHelper(plainEvent);
@ -127,7 +126,7 @@ describe("DownloadActionButton", () => {
stubClient();
fetchMockJest.getOnce("http://this.is.a.url/matrix.org/1234", "UFTUGJMF");
fetchMock.getOnce("http://this.is.a.url/matrix.org/1234", "UFTUGJMF");
const e2eEvent = new MatrixEvent({
room_id: "!room:id",

View File

@ -9,7 +9,7 @@ Please see LICENSE files in the repository root for full details.
import React from "react";
import { fireEvent, render, screen, waitFor, waitForElementToBeRemoved, within } from "jest-matrix-react";
import { EventType, getHttpUriForMxc, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import encrypt from "matrix-encrypt-attachment";
import { mocked } from "jest-mock";
import fs from "fs";
@ -121,7 +121,7 @@ describe("<MImageBody/>", () => {
withClientContextRenderOptions(cli),
);
expect(fetchMock).toHaveBeenCalledWith(url);
expect(fetchMock).toHaveFetched(url);
await screen.findByText("Error downloading image");
});
@ -167,7 +167,7 @@ describe("<MImageBody/>", () => {
expect(screen.getByText("Show image")).toBeInTheDocument();
expect(fetchMock).not.toHaveFetched(url);
expect(fetchMock).toHaveFetchedTimes(0, url);
});
it("should render hidden image placeholder", async () => {
@ -246,13 +246,9 @@ describe("<MImageBody/>", () => {
mocked(global.URL.createObjectURL).mockReturnValue("blob:generated-thumb");
fetchMock.getOnce(
"https://server/_matrix/media/v3/download/server/image",
{
body: fs.readFileSync(path.resolve(__dirname, "..", "..", "..", "images", "animated-logo.webp")),
},
{ sendAsJson: false },
);
fetchMock.getOnce("https://server/_matrix/media/v3/download/server/image", {
body: fs.readFileSync(path.resolve(__dirname, "..", "..", "..", "images", "animated-logo.webp")),
});
const event = new MatrixEvent({
room_id: "!room:server",

View File

@ -9,7 +9,7 @@ Please see LICENSE files in the repository root for full details.
import React from "react";
import { render, screen } from "jest-matrix-react";
import { EventType, getHttpUriForMxc, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import userEvent from "@testing-library/user-event";
import { RoomPermalinkCreator } from "../../../../../src/utils/permalinks/Permalinks";

View File

@ -9,7 +9,7 @@ Please see LICENSE files in the repository root for full details.
import React from "react";
import { EventType, getHttpUriForMxc, type IContent, type MatrixClient, MatrixEvent } from "matrix-js-sdk/src/matrix";
import { fireEvent, render, screen } from "jest-matrix-react";
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import { type MockedObject } from "jest-mock";
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
@ -170,7 +170,7 @@ describe("MVideoBody", () => {
expect(screen.getByText("Show video")).toBeInTheDocument();
expect(fetchMock).not.toHaveFetched(thumbUrl);
expect(fetchMock).toHaveFetchedTimes(0, thumbUrl);
});
it("should render video poster after user consent", async () => {

View File

@ -9,7 +9,7 @@ Please see LICENSE files in the repository root for full details.
import React from "react";
import { render, type RenderResult } from "jest-matrix-react";
import { type MatrixClient, type MatrixEvent, EventType, type Room, MsgType } from "matrix-js-sdk/src/matrix";
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import fs from "fs";
import path from "path";
@ -96,13 +96,9 @@ describe("MessageEvent", () => {
}
function mockMedia() {
fetchMock.getOnce(
"https://server/_matrix/media/v3/download/server/image",
{
body: fs.readFileSync(path.resolve(__dirname, "..", "..", "..", "images", "animated-logo.webp")),
},
{ sendAsJson: false },
);
fetchMock.getOnce("https://server/_matrix/media/v3/download/server/image", {
body: fs.readFileSync(path.resolve(__dirname, "..", "..", "..", "images", "animated-logo.webp")),
});
}
it("should render a TextualBody and an ImageBody", () => {

View File

@ -19,6 +19,7 @@ import {
mockClientMethodsUser,
mockIntlDateTimeFormat,
setupRoomWithPollEvents,
unmockIntlDateTimeFormat,
} from "../../../../../test-utils";
describe("<PollListItemEnded />", () => {
@ -52,14 +53,14 @@ describe("<PollListItemEnded />", () => {
const getComponent = (props: { event: MatrixEvent; poll: Poll }) =>
render(<PollListItemEnded {...props} onClick={jest.fn()} />);
beforeEach(() => {
beforeAll(() => {
// mock default locale to en-GB and set timezone
// so these tests run the same everywhere
mockIntlDateTimeFormat();
});
afterEach(() => {
jest.resetAllMocks();
afterAll(() => {
unmockIntlDateTimeFormat();
});
it("renders a poll with no responses", async () => {

View File

@ -56,7 +56,7 @@ exports[`RoomHeader dm does not show the face pile for DMs 1`] = `
style="--cpd-icon-button-size: 100%;"
>
<svg
aria-labelledby="_r_18c_"
aria-labelledby="_r_12c_"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
@ -83,7 +83,7 @@ exports[`RoomHeader dm does not show the face pile for DMs 1`] = `
style="--cpd-icon-button-size: 100%;"
>
<svg
aria-labelledby="_r_18h_"
aria-labelledby="_r_12h_"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
@ -98,7 +98,7 @@ exports[`RoomHeader dm does not show the face pile for DMs 1`] = `
</button>
<button
aria-label="Threads"
aria-labelledby="_r_18m_"
aria-labelledby="_r_12m_"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@ -125,7 +125,7 @@ exports[`RoomHeader dm does not show the face pile for DMs 1`] = `
</button>
<button
aria-label="Room info"
aria-labelledby="_r_18r_"
aria-labelledby="_r_12r_"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"

View File

@ -7,6 +7,7 @@ Please see LICENSE files in the repository root for full details.
*/
import { type IEventRelation, type MatrixEvent } from "matrix-js-sdk/src/matrix";
import { waitFor } from "jest-matrix-react";
import fetchMock from "@fetch-mock/jest";
import { TimelineRenderingType } from "../../../../../../../src/contexts/RoomContext";
import { mkStubRoom, stubClient } from "../../../../../../test-utils";
@ -27,7 +28,6 @@ const mockRoomState = {
const sendContentListToRoomSpy = jest.spyOn(ContentMessages.sharedInstance(), "sendContentListToRoom");
const sendContentToRoomSpy = jest.spyOn(ContentMessages.sharedInstance(), "sendContentToRoom");
const fetchSpy = jest.spyOn(window, "fetch");
const logSpy = jest.spyOn(console, "log").mockImplementation(() => {});
describe("handleClipboardEvent", () => {
@ -183,13 +183,12 @@ describe("handleClipboardEvent", () => {
const mockEventRelation = {} as unknown as IEventRelation;
handleClipboardEvent(originalEvent, originalEvent.clipboardData, mockRoomState, mockClient, mockEventRelation);
expect(fetchSpy).toHaveBeenCalledTimes(1);
expect(fetchSpy).toHaveBeenCalledWith("blob:");
expect(fetchMock).toHaveFetchedTimes(1, "blob:");
});
it("calls error handler when fetch fails", async () => {
const mockErrorMessage = "fetch failed";
fetchSpy.mockRejectedValueOnce(mockErrorMessage);
fetchMock.getOnce("blob:", { throws: new Error(mockErrorMessage) });
const originalEvent = createMockClipboardEvent({
type: "paste",
clipboardData: {
@ -214,12 +213,11 @@ describe("handleClipboardEvent", () => {
});
it("calls sendContentToRoom when parsing is successful", async () => {
fetchSpy.mockResolvedValueOnce({
url: "test/file",
fetchMock.get("test/file", {
blob: () => {
return Promise.resolve({ type: "image/jpeg" } as Blob);
},
} as Response);
});
const originalEvent = createMockClipboardEvent({
type: "paste",
@ -251,12 +249,11 @@ describe("handleClipboardEvent", () => {
});
it("calls error handler when parsing is not successful", async () => {
fetchSpy.mockResolvedValueOnce({
url: "test/file",
fetchMock.get("test/file", {
blob: () => {
return Promise.resolve({ type: "image/jpeg" } as Blob);
},
} as Response);
});
const mockErrorMessage = "sendContentToRoom failed";
sendContentToRoomSpy.mockRejectedValueOnce(mockErrorMessage);

View File

@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details.
import React from "react";
import { render, waitFor } from "jest-matrix-react";
import userEvent from "@testing-library/user-event";
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import SetIdServer from "../../../../../src/components/views/settings/SetIdServer";
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";

View File

@ -10,7 +10,7 @@ import React from "react";
import { act, render, screen, waitFor } from "jest-matrix-react";
import { mocked, type MockedObject } from "jest-mock";
import userEvent from "@testing-library/user-event";
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import { ThemeChoicePanel } from "../../../../../src/components/views/settings/ThemeChoicePanel";
import SettingsStore from "../../../../../src/settings/SettingsStore";

View File

@ -10,7 +10,7 @@ import { render } from "jest-matrix-react";
import { mocked } from "jest-mock";
import { type IClientWellKnown, type IServerVersions, type MatrixClient } from "matrix-js-sdk/src/matrix";
import React from "react";
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import LoginWithQRSection from "../../../../../../src/components/views/settings/devices/LoginWithQRSection";
import { MatrixClientPeg } from "../../../../../../src/MatrixClientPeg";

View File

@ -34,7 +34,7 @@ import {
type MatrixClient,
} from "matrix-js-sdk/src/matrix";
import { mocked, type MockedObject } from "jest-mock";
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import {
clearAllModals,
@ -1645,7 +1645,7 @@ describe("<SessionManagerTab />", () => {
],
});
mockCrypto.exportSecretsBundle = jest.fn();
fetchMock.mock(delegatedAuthConfig.jwks_uri!, {
fetchMock.route(delegatedAuthConfig.jwks_uri!, {
status: 200,
headers: {
"Content-Type": "application/json",

View File

@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import fetchMockJest from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import { mocked } from "jest-mock";
import { mediaFromMxc } from "../../../src/customisations/Media";
@ -20,7 +20,7 @@ describe("Media", () => {
(mxc) => `https://matrix.org/_matrix/media/r0/download/${mxc.slice(6)}`,
);
fetchMockJest.get("https://matrix.org/_matrix/media/r0/download/matrix.org/1234", {
fetchMock.get("https://matrix.org/_matrix/media/r0/download/matrix.org/1234", {
status: 404,
body: { errcode: "M_NOT_FOUND", error: "Not found" },
});

View File

@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
*/
import React from "react";
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import { type Translation } from "matrix-web-i18n";
import { type TranslationStringsObject } from "@matrix-org/react-sdk-module-api";
@ -28,7 +28,6 @@ import {
getLanguagesFromBrowser,
} from "../../src/languageHandler";
import { stubClient } from "../test-utils";
import { setupLanguageMock } from "../setup/setupLanguage";
async function setupTranslationOverridesForTests(overrides: TranslationStringsObject) {
const lookupUrl = "/translations.json";
@ -167,15 +166,13 @@ describe("languageHandler", () => {
describe("getAllLanguagesWithLabels", () => {
it("should handle unknown language sanely", async () => {
fetchMock.getOnce(
"/i18n/languages.json",
{
fetchMock.modifyRoute("languages", {
response: {
en: "en_EN.json",
de: "de_DE.json",
qq: "qq.json",
},
{ overwriteRoutes: true },
);
});
await expect(getAllLanguagesWithLabels()).resolves.toMatchInlineSnapshot(`
[
{
@ -195,7 +192,6 @@ describe("languageHandler", () => {
},
]
`);
setupLanguageMock(); // restore language mock
});
});
@ -290,10 +286,10 @@ describe("languageHandler JSX", function () {
});
describe("when translations exist in language", () => {
beforeEach(function () {
beforeEach(async () => {
stubClient();
setLanguage("en");
await setLanguage("en");
setMissingEntryGenerator((key) => key.split("|", 2)[1]);
});
@ -326,10 +322,10 @@ describe("languageHandler JSX", function () {
});
describe("for a non-en language", () => {
beforeEach(() => {
beforeEach(async () => {
stubClient();
setLanguage("lv");
// counterpart doesnt expose any way to restore default config
await setLanguage("lv");
// counterpart doesn't expose any way to restore default config
// missingEntryGenerator is mocked in the root setup file
// reset to default here
const counterpartDefaultMissingEntryGen = function (key: string) {

View File

@ -481,7 +481,14 @@ describe("ElementCall", () => {
let alice: RoomMember;
let roomSession: Mocked<MatrixRTCSession>;
function setRoomMembers(memberIds: string[]) {
jest.spyOn(room, "getJoinedMembers").mockReturnValue(memberIds.map((id) => ({ userId: id }) as RoomMember));
jest.spyOn(room, "getJoinedMembers").mockReturnValue(
memberIds.map(
(id) =>
({
userId: id,
}) as RoomMember,
),
);
}
beforeEach(() => {

View File

@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import fetchMockJest from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import { ClientEvent, MatrixClient } from "matrix-js-sdk/src/matrix";
import { SettingLevel } from "../../../../src/settings/SettingLevel";
@ -15,7 +15,7 @@ import SettingsStore from "../../../../src/settings/SettingsStore.ts";
describe("FallbackIceServerController", () => {
beforeEach(() => {
fetchMockJest.get("https://matrix.org/_matrix/client/versions", { versions: ["v1.4"] });
fetchMock.get("https://matrix.org/_matrix/client/versions", { versions: ["v1.4"] });
});
afterEach(() => {

View File

@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import { mocked } from "jest-mock";
import { OidcClient } from "oidc-client-ts";
import { logger } from "matrix-js-sdk/src/logger";
@ -188,13 +188,14 @@ describe("OidcClientStore", () => {
// spy and call through
jest.spyOn(OidcClient.prototype, "revokeToken").mockClear();
fetchMock.resetHistory();
fetchMock.clearHistory();
fetchMock.removeRoute("revocation-endpoint");
fetchMock.post(
authConfig.revocation_endpoint,
{
status: 200,
},
{ sendAsJson: true },
{ name: "revocation-endpoint" },
);
});
@ -219,21 +220,14 @@ describe("OidcClientStore", () => {
it("should still attempt to revoke refresh token when access token revocation fails", async () => {
// fail once, then succeed
fetchMock.removeRoute("revocation-endpoint");
fetchMock
.postOnce(
authConfig.revocation_endpoint,
{
status: 404,
},
{ overwriteRoutes: true, sendAsJson: true },
)
.post(
authConfig.revocation_endpoint,
{
status: 200,
},
{ sendAsJson: true },
);
.postOnce(authConfig.revocation_endpoint, {
status: 404,
})
.post(authConfig.revocation_endpoint, {
status: 200,
});
const store = new OidcClientStore(mockClient);

View File

@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
*/
import { mocked, type MockedObject } from "jest-mock";
import fetchMockJest from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import {
type MatrixClient,
ClientEvent,
@ -792,7 +792,7 @@ describe("ElementWidgetDriver", () => {
return null;
});
fetchMockJest.get("https://example.com/_matrix/media/v3/download/example.com/test_file", "test contents");
fetchMock.get("https://example.com/_matrix/media/v3/download/example.com/test_file", "test contents");
const result = await driver.downloadFile("mxc://example.com/test_file");
// A type test is impossible here because of

View File

@ -15,7 +15,7 @@ import {
TypedEventEmitter,
MatrixHttpApi,
} from "matrix-js-sdk/src/matrix";
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import { getMockClientWithEventEmitter, mockClientMethodsCrypto, mockPlatformPeg } from "../test-utils";
import { collectBugReport } from "../../src/rageshake/submit-rageshake";
@ -34,8 +34,6 @@ describe("Rageshakes", () => {
onlyData: true,
},
);
let windowSpy: jest.SpyInstance;
let mockWindow: Mocked<Window>;
beforeEach(() => {
mockClient = getMockClientWithEventEmitter({
@ -51,21 +49,8 @@ describe("Rageshakes", () => {
ed25519: "",
curve25519: "",
});
mockWindow = {
matchMedia: jest.fn().mockReturnValue({ matches: false }),
navigator: {
userAgent: "",
},
} as unknown as Mocked<Window>;
// @ts-ignore - We just need partial mock
windowSpy = jest.spyOn(global, "window", "get").mockReturnValue(mockWindow);
fetchMock.restore();
fetchMock.catch(404);
});
afterEach(() => {
windowSpy.mockRestore();
jest.spyOn(window, "matchMedia").mockReturnValue({ matches: false } as any);
});
describe("Basic Information", () => {
@ -97,7 +82,7 @@ describe("Rageshakes", () => {
];
it.each(mediaQueryTests)("should collect %s", async (_, query, label, matches) => {
mocked(mockWindow.matchMedia).mockImplementation((q): MediaQueryList => {
mocked(window.matchMedia).mockImplementation((q): MediaQueryList => {
if (q === query) {
return { matches: matches } as unknown as MediaQueryList;
}
@ -139,13 +124,13 @@ describe("Rageshakes", () => {
});
it("should collect user agent", async () => {
jest.replaceProperty(mockWindow.navigator, "userAgent", "jest navigator");
jest.spyOn(window.navigator, "userAgent", "get").mockReturnValue("jest navigator");
const formData = await collectBugReport();
const userAgent = formData.get("user_agent");
expect(userAgent).toBe("jest navigator");
// @ts-ignore - Need to force navigator to be undefined for test
jest.replaceProperty(mockWindow, "navigator", undefined);
jest.spyOn(window.navigator, "userAgent", "get").mockReturnValue(undefined);
const formDataWithoutNav = await collectBugReport();
expect(formDataWithoutNav.get("user_agent")).toBe("UNKNOWN");
});
@ -308,10 +293,6 @@ describe("Rageshakes", () => {
});
describe("Synapse info", () => {
beforeEach(() => {
fetchMock.reset();
});
it("should collect synapse admin keys if available", async () => {
fetchMock.get("path:/_synapse/admin/v1/server_version", {
server_version: "1.101.0 (b=matrix-org-hotfixes,6dbedcf601)",
@ -443,11 +424,9 @@ describe("Rageshakes", () => {
// @ts-ignore - Need to mock the safari
jest.replaceProperty(mockNavigator, "storage", undefined);
const mockDocument = {
const spy = jest.spyOn(global, "document", "get").mockReturnValue({
hasStorageAccess: jest.fn().mockReturnValue(true),
} as unknown as Mocked<Document>;
const spy = jest.spyOn(global, "document", "get").mockReturnValue(mockDocument);
} as any);
const formData = await collectBugReport();
expect(formData.get("storageManager_persisted")).toBe("true");
@ -487,14 +466,12 @@ describe("Rageshakes", () => {
crypto: true,
};
const disabledFeatures = ["cssanimations", "d0", "d1"];
const mockWindow = {
const windowSpy = jest.spyOn(global, "window", "get").mockReturnValue({
matchMedia: jest.fn().mockReturnValue({ matches: false }),
Modernizr: {
...allFeatures,
},
} as unknown as Mocked<Window>;
// @ts-ignore - We just need partial mock
const windowSpy = jest.spyOn(global, "window", "get").mockReturnValue(mockWindow);
} as any);
const formData = await collectBugReport();

View File

@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details.
import { AutoDiscovery, AutoDiscoveryAction, type ClientConfig } from "matrix-js-sdk/src/matrix";
import { logger } from "matrix-js-sdk/src/logger";
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import AutoDiscoveryUtils from "../../../src/utils/AutoDiscoveryUtils";
import { mockOpenIdConfiguration } from "../../test-utils/oidc";
@ -152,7 +152,7 @@ describe("AutoDiscoveryUtils", () => {
};
await expect(() =>
AutoDiscoveryUtils.buildValidatedConfigFromDiscovery(serverName, discoveryResult),
).rejects.toThrow("Invalid URL: banana");
).rejects.toThrow("Invalid URL");
});
it("uses hs url hostname when serverName is falsy in args and config", async () => {

View File

@ -6,7 +6,6 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import "core-js/stable/structured-clone"; // for idb access
import "fake-indexeddb/auto";
import { idbDelete, idbLoad, idbSave } from "../../../src/utils/StorageAccess";

View File

@ -20,7 +20,7 @@ import {
RoomMember,
RoomState,
} from "matrix-js-sdk/src/matrix";
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import escapeHtml from "escape-html";
import { type RelationsContainer } from "matrix-js-sdk/src/models/relations-container";
import { mocked } from "jest-mock";

View File

@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import { mocked } from "jest-mock";
import { TokenRefresher } from "../../../../src/utils/oidc/TokenRefresher";

View File

@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import { completeAuthorizationCodeGrant } from "matrix-js-sdk/src/oidc/authorize";
import * as randomStringUtils from "matrix-js-sdk/src/randomstring";
import { type BearerTokenResponse } from "matrix-js-sdk/src/oidc/validate";
@ -58,9 +58,7 @@ describe("OIDC authorization", () => {
subtle: webCrypto.subtle,
},
});
});
beforeAll(() => {
fetchMock.get(`${delegatedAuthConfig.issuer}.well-known/openid-configuration`, delegatedAuthConfig);
});

View File

@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import fetchMockJest from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import { OidcError } from "matrix-js-sdk/src/oidc/error";
import { type OidcClientConfig } from "matrix-js-sdk/src/matrix";
@ -28,8 +28,7 @@ describe("getOidcClientId()", () => {
const delegatedAuthConfig = makeDelegatedAuthConfig(issuer);
beforeEach(() => {
fetchMockJest.mockClear();
fetchMockJest.resetBehavior();
fetchMock.removeRoutes();
mockPlatformPeg();
Object.defineProperty(PlatformPeg.get(), "baseUrl", {
get(): string {
@ -51,7 +50,7 @@ describe("getOidcClientId()", () => {
it("should return static clientId when configured", async () => {
expect(await getOidcClientId(delegatedAuthConfig, staticOidcClients)).toEqual("abc123");
// didn't try to register
expect(fetchMockJest).toHaveFetchedTimes(0);
expect(fetchMock).toHaveFetchedTimes(0);
});
it("should throw when no static clientId is configured and no registration endpoint", async () => {
@ -63,7 +62,7 @@ describe("getOidcClientId()", () => {
OidcError.DynamicRegistrationNotSupported,
);
// didn't try to register
expect(fetchMockJest).toHaveFetchedTimes(0);
expect(fetchMock).toHaveFetchedTimes(0);
});
it("should handle when staticOidcClients object is falsy", async () => {
@ -75,28 +74,23 @@ describe("getOidcClientId()", () => {
OidcError.DynamicRegistrationNotSupported,
);
// didn't try to register
expect(fetchMockJest).toHaveFetchedTimes(0);
expect(fetchMock).toHaveFetchedTimes(0);
});
it("should make correct request to register client", async () => {
fetchMockJest.post(delegatedAuthConfig.registration_endpoint!, {
fetchMock.post(delegatedAuthConfig.registration_endpoint!, {
status: 200,
body: JSON.stringify({ client_id: dynamicClientId }),
});
expect(await getOidcClientId(delegatedAuthConfig)).toEqual(dynamicClientId);
// didn't try to register
expect(fetchMockJest).toHaveBeenCalledWith(
delegatedAuthConfig.registration_endpoint!,
expect.objectContaining({
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
},
method: "POST",
}),
);
expect(JSON.parse(fetchMockJest.mock.calls[0][1]!.body as string)).toEqual(
expect.objectContaining({
expect(fetchMock).toHaveFetched(delegatedAuthConfig.registration_endpoint!, {
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
},
method: "POST",
body: {
client_name: clientName,
client_uri: baseUrl,
response_types: ["code"],
@ -106,19 +100,19 @@ describe("getOidcClientId()", () => {
token_endpoint_auth_method: "none",
application_type: "web",
logo_uri: `${baseUrl}/vector-icons/1024.png`,
}),
);
},
});
});
it("should throw when registration request fails", async () => {
fetchMockJest.post(delegatedAuthConfig.registration_endpoint!, {
fetchMock.post(delegatedAuthConfig.registration_endpoint!, {
status: 500,
});
await expect(getOidcClientId(delegatedAuthConfig)).rejects.toThrow(OidcError.DynamicRegistrationFailed);
});
it("should throw when registration response is invalid", async () => {
fetchMockJest.post(delegatedAuthConfig.registration_endpoint!, {
fetchMock.post(delegatedAuthConfig.registration_endpoint!, {
status: 200,
// no clientId in response
body: "{}",

View File

@ -6,12 +6,10 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import { getVectorConfig } from "../../../src/vector/getconfig";
fetchMock.config.overwriteRoutes = true;
describe("getVectorConfig()", () => {
const elementDomain = "app.element.io";
const now = 1234567890;
@ -31,7 +29,7 @@ describe("getVectorConfig()", () => {
// stable value for cachebuster
jest.spyOn(Date, "now").mockReturnValue(now);
jest.clearAllMocks();
fetchMock.mockClear();
fetchMock.removeRoutes();
});
afterAll(() => {
@ -39,15 +37,15 @@ describe("getVectorConfig()", () => {
});
it("requests specific config for document domain", async () => {
fetchMock.getOnce("express:/config.app.element.io.json", specificConfig);
fetchMock.getOnce("express:/config.json", generalConfig);
fetchMock.getOnce("express:/config.app.element.io.json*", specificConfig);
fetchMock.getOnce("express:/config.json*", generalConfig);
await expect(getVectorConfig()).resolves.toEqual(specificConfig);
});
it("adds trailing slash to relativeLocation when not an empty string", async () => {
fetchMock.getOnce("express:../config.app.element.io.json", specificConfig);
fetchMock.getOnce("express:../config.json", generalConfig);
fetchMock.getOnce("express:/config.app.element.io.json", specificConfig);
fetchMock.getOnce("express:/config.json", generalConfig);
await expect(getVectorConfig("..")).resolves.toEqual(specificConfig);
});

View File

@ -1,5 +1,5 @@
/**
* @jest-environment jsdom
* @jest-environment jest-fixed-jsdom
* @jest-environment-options {"url": "https://app.element.io/?loginToken=123&state=abc&code=xyz&no_universal_links&something_else=value"}
*/
@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import { waitFor, screen } from "jest-matrix-react";
import { loadApp, showError, showIncompatibleBrowser } from "../../../src/vector/init.tsx";

View File

@ -19,7 +19,6 @@ import { BreadcrumbsStore } from "../../../../src/stores/BreadcrumbsStore";
import Modal from "../../../../src/Modal";
import DesktopCapturerSourcePicker from "../../../../src/components/views/elements/DesktopCapturerSourcePicker";
import ElectronPlatform from "../../../../src/vector/platform/ElectronPlatform";
import { setupLanguageMock } from "../../../setup/setupLanguage";
import { stubClient } from "../../../test-utils";
import ToastStore from "../../../../src/stores/ToastStore.ts";
@ -57,7 +56,6 @@ describe("ElectronPlatform", () => {
window.electron = mockElectron;
jest.clearAllMocks();
Object.defineProperty(window, "navigator", { value: { userAgent: defaultUserAgent }, writable: true });
setupLanguageMock();
});
const getElectronEventHandlerCall = (
@ -456,7 +454,7 @@ describe("ElectronPlatform", () => {
await waitFor(() => {
const ipcMessage = mockElectron.send.mock.lastCall;
expect(ipcMessage?.[1]).toEqual(1);
expect(ipcMessage?.[2] instanceof ArrayBuffer).toEqual(true);
expect(ipcMessage?.[2].constructor.name).toEqual("ArrayBuffer");
});
});
@ -473,10 +471,10 @@ describe("ElectronPlatform", () => {
);
expect(ipcMessageA?.[1]).toEqual(1);
expect(ipcMessageA?.[2] instanceof ArrayBuffer).toEqual(true);
expect(ipcMessageA?.[2].constructor.name).toEqual("ArrayBuffer");
expect(ipcMessageB?.[1]).toEqual(2);
expect(ipcMessageB?.[2] instanceof ArrayBuffer).toEqual(true);
expect(ipcMessageB?.[2].constructor.name).toEqual("ArrayBuffer");
});
});
it("should remove badge when notification count zeros", async () => {
@ -491,7 +489,7 @@ describe("ElectronPlatform", () => {
);
expect(ipcMessageA?.[1]).toEqual(1);
expect(ipcMessageA?.[2] instanceof ArrayBuffer).toEqual(true);
expect(ipcMessageA?.[2].constructor.name).toEqual("ArrayBuffer");
expect(ipcMessageB?.[1]).toEqual(0);
expect(ipcMessageB?.[2]).toBeNull();
@ -506,7 +504,7 @@ describe("ElectronPlatform", () => {
const ipcMessage = mockElectron.send.mock.calls.find((call) => call[0] === "setBadgeCount");
expect(ipcMessage?.[1]).toEqual(0);
expect(ipcMessage?.[2] instanceof ArrayBuffer).toEqual(true);
expect(ipcMessage?.[2].constructor.name).toEqual("ArrayBuffer");
expect(ipcMessage?.[3]).toEqual(true);
});
});
@ -522,7 +520,7 @@ describe("ElectronPlatform", () => {
);
expect(ipcMessageA?.[1]).toEqual(0);
expect(ipcMessageA?.[2] instanceof ArrayBuffer).toEqual(true);
expect(ipcMessageA?.[2].constructor.name).toEqual("ArrayBuffer");
expect(ipcMessageA?.[3]).toEqual(true);
expect(ipcMessageB?.[1]).toEqual(0);

View File

@ -6,23 +6,28 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import { UpdateCheckStatus } from "../../../../src/BasePlatform";
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
import WebPlatform from "../../../../src/vector/platform/WebPlatform";
import { setupLanguageMock } from "../../../setup/setupLanguage";
import ToastStore from "../../../../src/stores/ToastStore.ts";
import defaultDispatcher from "../../../../src/dispatcher/dispatcher.ts";
import { emitPromise } from "../../../test-utils";
import { Action } from "../../../../src/dispatcher/actions.ts";
fetchMock.config.overwriteRoutes = true;
describe("WebPlatform", () => {
beforeEach(() => {
jest.clearAllMocks();
setupLanguageMock();
jest.spyOn(global, "navigator", "get").mockReturnValue({
...navigator,
// @ts-expect-error - mocking readonly object
serviceWorker: {
register: jest.fn().mockResolvedValue({
update: jest.fn(),
}),
addEventListener: jest.fn(),
},
});
});
it("returns human readable name", () => {
@ -32,22 +37,17 @@ describe("WebPlatform", () => {
describe("service worker", () => {
it("registers successfully", () => {
// @ts-expect-error - mocking readonly object
navigator.serviceWorker = {
register: jest.fn().mockResolvedValue({
update: jest.fn(),
}),
addEventListener: jest.fn(),
};
new WebPlatform();
expect(navigator.serviceWorker.register).toHaveBeenCalled();
});
it("handles errors", async () => {
// @ts-expect-error - mocking readonly object
navigator.serviceWorker = {
register: undefined,
};
jest.spyOn(global, "navigator", "get").mockReturnValue({
serviceWorker: {
// @ts-expect-error - mocking readonly object
register: undefined,
},
});
new WebPlatform();
defaultDispatcher.dispatch({ action: Action.ClientStarted });
@ -85,7 +85,7 @@ describe("WebPlatform", () => {
"develop.element.io: Chrome on macOS",
],
])("%s & %s = %s", (url, userAgent, result) => {
Object.defineProperty(window, "navigator", { value: { userAgent }, writable: true });
jest.spyOn(global, "navigator", "get").mockReturnValue({ userAgent } as Navigator);
Object.defineProperty(window, "location", { value: { href: url }, writable: true });
const platform = new WebPlatform();
expect(platform.getDefaultDeviceDisplayName()).toEqual(result);
@ -174,7 +174,7 @@ describe("WebPlatform", () => {
it("should return not available and call showNoUpdate when current version matches most recent version", async () => {
// @ts-ignore
WebPlatform.VERSION = prodVersion;
fetchMock.getOnce("/version", prodVersion);
fetchMock.getOnce("end:/version", prodVersion);
const platform = new WebPlatform();
const showUpdate = jest.fn();
@ -189,7 +189,7 @@ describe("WebPlatform", () => {
it("should strip v prefix from versions before comparing", async () => {
// @ts-ignore
WebPlatform.VERSION = prodVersion;
fetchMock.getOnce("/version", `v${prodVersion}`);
fetchMock.getOnce("end:/version", `v${prodVersion}`);
const platform = new WebPlatform();
const showUpdate = jest.fn();
@ -207,7 +207,7 @@ describe("WebPlatform", () => {
async () => {
// @ts-ignore
WebPlatform.VERSION = "0.0.0"; // old version
fetchMock.getOnce("/version", prodVersion);
fetchMock.getOnce("end:/version", prodVersion);
const platform = new WebPlatform();
const showUpdate = jest.fn();
@ -224,7 +224,7 @@ describe("WebPlatform", () => {
// @ts-ignore
WebPlatform.VERSION = "0.0.0"; // old version
jest.spyOn(MatrixClientPeg, "userRegisteredWithinLastHours").mockReturnValue(true);
fetchMock.getOnce("/version", prodVersion);
fetchMock.getOnce("end:/version", prodVersion);
const platform = new WebPlatform();
const showUpdate = jest.fn();
@ -237,7 +237,7 @@ describe("WebPlatform", () => {
});
it("should return error when version check fails", async () => {
fetchMock.getOnce("/version", { throws: "oups" });
fetchMock.getOnce("end:/version", { throws: "oups" });
const platform = new WebPlatform();
const showUpdate = jest.fn();

View File

@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details.
import { Room } from "matrix-js-sdk/src/matrix";
import { logger } from "matrix-js-sdk/src/logger";
import fetchMock from "fetch-mock-jest";
import fetchMock from "@fetch-mock/jest";
import { addManagedHybridWidget, isManagedHybridWidgetEnabled } from "../../../src/widgets/ManagedHybrid";
import { stubClient } from "../../test-utils";
@ -63,7 +63,7 @@ describe("addManagedHybridWidget", () => {
fetchMock.mockClear();
await addManagedHybridWidget(room);
expect(logSpy).toHaveBeenCalledWith("User not allowed to modify widgets in !room:server");
expect(fetchMock).toHaveBeenCalledTimes(0);
expect(fetchMock).toHaveFetchedTimes(0);
});
it("should noop if no widget_build_url", async () => {
@ -71,7 +71,7 @@ describe("addManagedHybridWidget", () => {
fetchMock.mockClear();
await addManagedHybridWidget(room);
expect(fetchMock).toHaveBeenCalledTimes(0);
expect(fetchMock).toHaveFetchedTimes(0);
});
it("should add the widget successfully", async () => {
@ -87,7 +87,7 @@ describe("addManagedHybridWidget", () => {
});
await addManagedHybridWidget(room);
expect(fetchMock).toHaveBeenCalledWith("https://widget-build-url?roomId=!room:server");
expect(fetchMock).toHaveFetched("https://widget-build-url?roomId=!room:server");
expect(setRoomWidgetContentSpy).toHaveBeenCalledWith(room.client, room.roomId, "WIDGET_ID", {
"key": "value",
"io.element.managed_hybrid": true,

110
yarn.lock
View File

@ -51,7 +51,7 @@
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.28.5.tgz#a8a4962e1567121ac0b3b487f52107443b455c7f"
integrity sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==
"@babel/core@^7.0.0", "@babel/core@^7.12.10", "@babel/core@^7.18.5", "@babel/core@^7.21.3", "@babel/core@^7.23.9", "@babel/core@^7.24.4", "@babel/core@^7.27.4", "@babel/core@^7.28.0":
"@babel/core@^7.12.10", "@babel/core@^7.18.5", "@babel/core@^7.21.3", "@babel/core@^7.23.9", "@babel/core@^7.24.4", "@babel/core@^7.27.4", "@babel/core@^7.28.0":
version "7.28.5"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.28.5.tgz#4c81b35e51e1b734f510c99b07dfbc7bbbb48f7e"
integrity sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==
@ -1771,6 +1771,13 @@
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2"
integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==
"@fetch-mock/jest@^0.2.20":
version "0.2.20"
resolved "https://registry.yarnpkg.com/@fetch-mock/jest/-/jest-0.2.20.tgz#85dc2dd9afb9022a6e0eda1d9f3351f3a5fa70c5"
integrity sha512-DGX2bhBInodaWPMV3+UZ530aVM3wDj16sAPjFzkrwb0JwNWIQK07CNbYprQ3Tmd2ixDJeaNx2E0aNb+hRb8FFA==
dependencies:
fetch-mock "^12.6.0"
"@floating-ui/core@^1.7.3":
version "1.7.3"
resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.7.3.tgz#462d722f001e23e46d86fd2bd0d21b7693ccb8b7"
@ -3546,7 +3553,7 @@
resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.16.tgz#8ebe53d69efada7044454e3305c19017d97ced2a"
integrity sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==
"@types/glob-to-regexp@^0.4.1":
"@types/glob-to-regexp@^0.4.1", "@types/glob-to-regexp@^0.4.4":
version "0.4.4"
resolved "https://registry.yarnpkg.com/@types/glob-to-regexp/-/glob-to-regexp-0.4.4.tgz#409e71290253203185b1ea8a3d6ea406a4bdc902"
integrity sha512-nDKoaKJYbnn1MZxUY0cA1bPmmgZbg0cTq7Rh13d0KWYNOiKbqoR+2d89SnRPszGh7ROzSwZ/GOjZ4jPbmmZ6Eg==
@ -3657,14 +3664,6 @@
resolved "https://registry.yarnpkg.com/@types/modernizr/-/modernizr-3.5.6.tgz#c50d64a73edc30284679f09ad54e7d095e69f2a0"
integrity sha512-yslwR0zZ3zAT1qXcCPxIcD23CZ6W6nKsl6JufSJHAmdwOBuYwCVJkaMsEo9yzxGV7ATfoX8S+RgtnajOEtKxYA==
"@types/node-fetch@^2.6.2":
version "2.6.13"
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.13.tgz#e0c9b7b5edbdb1b50ce32c127e85e880872d56ee"
integrity sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==
dependencies:
"@types/node" "*"
form-data "^4.0.4"
"@types/node-forge@^1.3.0":
version "1.3.11"
resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-1.3.11.tgz#0972ea538ddb0f4d9c2fa0ec5db5724773a604da"
@ -5653,7 +5652,7 @@ core-js-compat@^3.43.0:
dependencies:
browserslist "^4.26.3"
core-js@^3.0.0, core-js@^3.38.1:
core-js@^3.38.1:
version "3.47.0"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.47.0.tgz#436ef07650e191afeb84c24481b298bd60eb4a17"
integrity sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==
@ -7234,28 +7233,15 @@ fdir@^6.4.4, fdir@^6.5.0:
resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350"
integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==
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==
fetch-mock@12, fetch-mock@^12.6.0:
version "12.6.0"
resolved "https://registry.yarnpkg.com/fetch-mock/-/fetch-mock-12.6.0.tgz#e5ed5d471eeeb29478260ce48385dca6773b105e"
integrity sha512-oAy0OqAvjAvduqCeWveBix7LLuDbARPqZZ8ERYtBcCURA3gy7EALA3XWq0tCNxsSg+RmmJqyaeeZlOCV9abv6w==
dependencies:
fetch-mock "^9.11.0"
fetch-mock@9.11.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"
"@types/glob-to-regexp" "^0.4.4"
dequal "^2.0.3"
glob-to-regexp "^0.4.1"
regexparam "^3.0.0"
fflate@^0.4.8:
version "0.4.8"
@ -7632,7 +7618,7 @@ glob-parent@^6.0.1, 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==
@ -8483,11 +8469,6 @@ is-string@^1.1.1:
call-bound "^1.0.3"
has-tostringtag "^1.0.2"
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-symbol@^1.0.4, is-symbol@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.1.1.tgz#f47761279f532e2b05a7024a7506dbbedacd0634"
@ -8755,7 +8736,7 @@ jest-each@30.2.0:
jest-util "30.2.0"
pretty-format "30.2.0"
jest-environment-jsdom@^30.0.0:
jest-environment-jsdom@^30.2.0:
version "30.2.0"
resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-30.2.0.tgz#e95e0921ed22be974f1d8a324766d12b1844cb2c"
integrity sha512-zbBTiqr2Vl78pKp/laGBREYzbZx9ZtqPjOK4++lL4BNDhxRnahg51HtoDrk9/VjIy9IthNEWdKVd7H5bqBhiWQ==
@ -8779,6 +8760,11 @@ jest-environment-node@30.2.0:
jest-util "30.2.0"
jest-validate "30.2.0"
jest-fixed-jsdom@^0.0.11:
version "0.0.11"
resolved "https://registry.yarnpkg.com/jest-fixed-jsdom/-/jest-fixed-jsdom-0.0.11.tgz#67b5d5c4e9821bfb1e09a43139bfb0b9f4ec4f18"
integrity sha512-3UkjgM79APnmLVDnelrxdwz4oybD5qw6NLyayl7iCX8C8tJHeqjL9fmNrRlIrNiVJSXkF5t9ZPJ+xlM0kSwwYg==
jest-haste-map@30.2.0:
version "30.2.0"
resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-30.2.0.tgz#808e3889f288603ac70ff0ac047598345a66022e"
@ -9413,11 +9399,6 @@ 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.memoize@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
@ -9428,11 +9409,6 @@ lodash.merge@^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==
lodash.truncate@^4.4.2:
version "4.4.2"
resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
@ -10403,11 +10379,6 @@ path-to-regexp@0.1.12:
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7"
integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==
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-to-regexp@^8.0.0:
version "8.3.0"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-8.3.0.tgz#aa818a6981f99321003a08987d3cec9c3474cd1f"
@ -11321,11 +11292,6 @@ qs@^6.14.0:
dependencies:
side-channel "^1.1.0"
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==
queue-microtask@^1.2.2:
version "1.2.3"
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
@ -11699,6 +11665,11 @@ regexp.prototype.flags@^1.5.3, regexp.prototype.flags@^1.5.4:
gopd "^1.2.0"
set-function-name "^2.0.2"
regexparam@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/regexparam/-/regexparam-3.0.0.tgz#1673e09d41cb7fd41eaafd4040a6aa90daa0a21a"
integrity sha512-RSYAtP31mvYLkAHrOlh25pCNQ5hWnT106VukGaaFfuJrZFkGRX5GhUAdPqpSDXxOhA2c4akmRuplv1mRqnBn6Q==
regexpu-core@^6.3.1:
version "6.4.0"
resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-6.4.0.tgz#3580ce0c4faedef599eccb146612436b62a176e5"
@ -13134,13 +13105,6 @@ tough-cookie@^5.1.1:
dependencies:
tldts "^6.1.32"
tr46@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09"
integrity sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==
dependencies:
punycode "^2.1.0"
tr46@^5.1.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-5.1.1.tgz#96ae867cddb8fdb64a49cc3059a8d428bcf238ca"
@ -13625,11 +13589,6 @@ webidl-conversions@^3.0.0:
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"
@ -13835,15 +13794,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.1.0, which-boxed-primitive@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz#d76ec27df7fa165f18d5808374a5fe23c29b176e"