Update react monorepo to v19 (major) (#28914)

* Update react monorepo to v19

* Import JSX explicitly for React 19 compatibility

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

* Update usages of refs for React 19 compatibility

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

* Update react imports

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

* Avoid legacy contexts as much as possible

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

* Avoid deprecated React symbols

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

* Stash

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

* Update usages of refs for React 19 compatibility

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

* Iterate

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

* Iterate

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

* Switch pillify to use a html-react-parser approach rather than DOM muddling

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

* Iterate

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

* Iterate react html parsing

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

* Iterate react html parsing

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

* Iterate html parsing

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

* Memoize the EventContentBody component

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

* Iterate html parsing

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

* Iterate

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

* Iterate

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

* Simplify

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

* Iterate

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

* Discard changes to src/Linkify.tsx

* Discard changes to src/components/views/messages/TextualBody.tsx

* Discard changes to src/settings/handlers/AbstractLocalStorageSettingsHandler.ts

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Prepare for React 19 upgrade

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

* Iterate

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

* Remove stale comment

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
renovate[bot]
2025-04-09 19:03:09 +00:00
committed by GitHub
parent f54fbf7231
commit e1b2e3a101
44 changed files with 300 additions and 290 deletions

View File

@@ -63,12 +63,12 @@ describe("AutocompleteInput", () => {
const input = getEditorInput();
act(() => {
await act(async () => {
fireEvent.focus(input);
fireEvent.change(input, { target: { value: "user" } });
await waitFor(() => expect(mockProvider.getCompletions).toHaveBeenCalledTimes(1));
});
await waitFor(() => expect(mockProvider.getCompletions).toHaveBeenCalledTimes(1));
expect(screen.getByTestId("autocomplete-matches").childNodes).toHaveLength(mockCompletion.length);
});
@@ -152,12 +152,12 @@ describe("AutocompleteInput", () => {
const input = getEditorInput();
act(() => {
await act(async () => {
fireEvent.focus(input);
fireEvent.change(input, { target: { value: "user" } });
await waitFor(() => expect(mockProvider.getCompletions).toHaveBeenCalledTimes(1));
});
await waitFor(() => expect(mockProvider.getCompletions).toHaveBeenCalledTimes(1));
expect(screen.getAllByTestId("custom-suggestion-element")).toHaveLength(mockCompletion.length);
});

View File

@@ -118,7 +118,7 @@ describe("<MatrixChat />", () => {
startup: jest.fn(),
},
login: jest.fn(),
loginFlows: jest.fn(),
loginFlows: jest.fn().mockResolvedValue({ flows: [] }),
isGuest: jest.fn().mockReturnValue(false),
clearStores: jest.fn(),
setGuest: jest.fn(),
@@ -165,12 +165,9 @@ describe("<MatrixChat />", () => {
isNameResolvable: true,
warning: "",
};
let initPromise: Promise<void> | undefined;
let defaultProps: ComponentProps<typeof MatrixChat>;
const getComponent = (props: Partial<ComponentProps<typeof MatrixChat>> = {}) => {
// MatrixChat does many questionable things which bomb tests in modern React mode,
// we'll want to refactor and break up MatrixChat before turning off legacyRoot mode
return render(<MatrixChat {...defaultProps} {...props} />, { legacyRoot: true });
return render(<MatrixChat {...defaultProps} {...props} />);
};
// make test results readable
@@ -220,6 +217,8 @@ describe("<MatrixChat />", () => {
};
beforeEach(async () => {
localStorage.clear();
jest.restoreAllMocks();
defaultProps = {
config: {
brand: "Test",
@@ -235,10 +234,8 @@ describe("<MatrixChat />", () => {
onNewScreen: jest.fn(),
onTokenLoginCompleted: jest.fn(),
realQueryParams: {},
initPromiseCallback: (p: Promise<void>) => (initPromise = p),
};
initPromise = undefined;
mockClient = getMockClientWithEventEmitter(getMockClientMethods());
jest.spyOn(MatrixJs, "createClient").mockReturnValue(mockClient);
@@ -257,18 +254,9 @@ describe("<MatrixChat />", () => {
});
afterEach(async () => {
// Wait for the promise that MatrixChat gives us to complete so that we know
// it's finished running its login code. We either need to do this or make the
// login code abort halfway through once the test finishes testing whatever it
// needs to test. If we do nothing, the login code will just continue running
// and interfere with the subsequent tests.
await initPromise;
// @ts-ignore
DMRoomMap.setShared(null);
jest.restoreAllMocks();
// emit a loggedOut event so that all of the Store singletons forget about their references to the mock client
// (must be sync otherwise the next test will start before it happens)
act(() => defaultDispatcher.dispatch({ action: Action.OnLoggedOut }, true));
@@ -313,7 +301,6 @@ describe("<MatrixChat />", () => {
state: state,
};
const userId = "@alice:server.org";
const deviceId = "test-device-id";
const accessToken = "test-access-token-from-oidc";
@@ -335,8 +322,6 @@ describe("<MatrixChat />", () => {
const dialog = await screen.findByRole("dialog");
expect(within(dialog).getByText(errorMessage)).toBeInTheDocument();
// just check we're back on welcome page
await expect(screen.findByTestId("mx_welcome_screen")).resolves.toBeInTheDocument();
};
beforeEach(() => {
@@ -518,6 +503,9 @@ describe("<MatrixChat />", () => {
it("should set logged in and start MatrixClient", async () => {
getComponent({ realQueryParams });
defaultDispatcher.dispatch({
action: "will_start_client",
});
// client successfully started
await waitFor(() =>
expect(defaultDispatcher.dispatch).toHaveBeenCalledWith({ action: "client_started" }),
@@ -597,9 +585,7 @@ describe("<MatrixChat />", () => {
// wait for logged in view to load
await screen.findByLabelText("User menu");
expect(screen.queryByRole("progressbar")).not.toBeInTheDocument();
const h1Element = screen.getByRole("heading", { level: 1 });
expect(h1Element).toHaveTextContent(`Welcome Ernie`);
await screen.findByRole("heading", { level: 1, name: "Welcome Ernie" });
});
describe("clean up drafts", () => {
@@ -612,9 +598,6 @@ describe("<MatrixChat />", () => {
localStorage.setItem(`mx_cider_state_${roomId}`, "fake_content");
mockClient.getRoom.mockImplementation((id) => [room].find((room) => room.roomId === id) || null);
});
afterEach(() => {
jest.restoreAllMocks();
});
it("should clean up drafts", async () => {
Date.now = jest.fn(() => timestamp);
localStorage.setItem(`mx_cider_state_${roomId}`, "fake_content");
@@ -1113,8 +1096,6 @@ describe("<MatrixChat />", () => {
"org.matrix.e2e_cross_signing",
);
await flushPromises();
// logged in
await screen.findByLabelText("User menu");
});
@@ -1210,7 +1191,6 @@ describe("<MatrixChat />", () => {
};
let loginClient!: ReturnType<typeof getMockClientWithEventEmitter>;
const userId = "@alice:server.org";
const deviceId = "test-device-id";
const accessToken = "test-access-token";
const clientLoginResponse = {
@@ -1304,6 +1284,7 @@ describe("<MatrixChat />", () => {
},
);
});
it("should clear storage", async () => {
const localStorageClearSpy = jest.spyOn(localStorage.__proto__, "clear");
@@ -1349,6 +1330,9 @@ describe("<MatrixChat />", () => {
it("should continue to post login setup when no session is found in local storage", async () => {
getComponent({ realQueryParams });
defaultDispatcher.dispatch({
action: "will_start_client",
});
// logged in but waiting for sync screen
await screen.findByText("Logout");
@@ -1604,8 +1588,13 @@ describe("<MatrixChat />", () => {
});
await flushPromises();
mockClient.emit(CryptoEvent.KeyBackupFailed, "error code");
await waitFor(() => expect(spy).toHaveBeenCalledTimes(1));
expect((spy.mock.lastCall![0] as any)._payload._result).toEqual(expect.objectContaining({ __test: true }));
await waitFor(() =>
expect(spy).toHaveBeenCalledWith(
expect.objectContaining({
_payload: expect.objectContaining({ _result: expect.objectContaining({ __test: true }) }),
}),
),
);
});
it("should show the recovery method removed dialog", async () => {
@@ -1622,8 +1611,13 @@ describe("<MatrixChat />", () => {
});
await flushPromises();
mockClient.emit(CryptoEvent.KeyBackupFailed, "error code");
await waitFor(() => expect(spy).toHaveBeenCalledTimes(1));
expect((spy.mock.lastCall![0] as any)._payload._result).toEqual(expect.objectContaining({ __test: true }));
await waitFor(() =>
expect(spy).toHaveBeenCalledWith(
expect.objectContaining({
_payload: expect.objectContaining({ _result: expect.objectContaining({ __test: true }) }),
}),
),
);
});
});
});

View File

@@ -148,6 +148,8 @@ describe("PipContainer", () => {
WidgetStore.instance.addVirtualWidget(call.widget, room.roomId);
WidgetMessagingStore.instance.storeMessaging(widget, room.roomId, {
stop: () => {},
hasCapability: jest.fn(),
feedStateUpdate: jest.fn().mockResolvedValue(undefined),
} as unknown as ClientWidgetApi);
await call.start();
@@ -271,7 +273,10 @@ describe("PipContainer", () => {
Parameters<ClientWidgetApi["transport"]["send"]>
>()
.mockResolvedValue({});
const mockMessaging = { transport: { send: sendSpy }, stop: () => {} } as unknown as ClientWidgetApi;
const mockMessaging = {
transport: { send: sendSpy },
stop: () => {},
} as unknown as ClientWidgetApi;
WidgetMessagingStore.instance.storeMessaging(new Widget(widget), room.roomId, mockMessaging);
await user.click(screen.getByRole("button", { name: "Leave" }));
expect(sendSpy).toHaveBeenCalledWith(ElementWidgetActions.HangupCall, {});

View File

@@ -108,9 +108,7 @@ exports[`MessagePanel should handle lots of membership events quickly 1`] = `
<span
class="mx_TextualEvent mx_GenericEventListSummary_summary"
>
<span>
@user:id made no changes 100 times
</span>
@user:id made no changes 100 times
</span>
</div>
</div>