From 2c30e6d909b499ff10df7a147cda2d76ffbf0449 Mon Sep 17 00:00:00 2001 From: Michael Weimann Date: Wed, 8 Feb 2023 09:31:24 +0100 Subject: [PATCH 1/5] Migrate `ZoomButtons-test` to react-testing-library (#10104) --- src/components/views/location/ZoomButtons.tsx | 4 +- .../views/location/ZoomButtons-test.tsx | 21 ++--- .../LocationViewDialog-test.tsx.snap | 8 +- .../__snapshots__/ZoomButtons-test.tsx.snap | 76 +++++-------------- 4 files changed, 29 insertions(+), 80 deletions(-) diff --git a/src/components/views/location/ZoomButtons.tsx b/src/components/views/location/ZoomButtons.tsx index 4c707979bce1..461cdad3cdbe 100644 --- a/src/components/views/location/ZoomButtons.tsx +++ b/src/components/views/location/ZoomButtons.tsx @@ -39,7 +39,7 @@ const ZoomButtons: React.FC = ({ map }) => {
@@ -47,7 +47,7 @@ const ZoomButtons: React.FC = ({ map }) => { diff --git a/test/components/views/location/ZoomButtons-test.tsx b/test/components/views/location/ZoomButtons-test.tsx index 831860bcd5b4..8fbe6710ebd9 100644 --- a/test/components/views/location/ZoomButtons-test.tsx +++ b/test/components/views/location/ZoomButtons-test.tsx @@ -1,5 +1,5 @@ /* -Copyright 2022 The Matrix.org Foundation C.I.C. +Copyright 2022, 2023 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,13 +15,10 @@ limitations under the License. */ import React from "react"; -// eslint-disable-next-line deprecate/import -import { mount } from "enzyme"; import * as maplibregl from "maplibre-gl"; -import { act } from "react-dom/test-utils"; +import { render, screen } from "@testing-library/react"; import ZoomButtons from "../../../../src/components/views/location/ZoomButtons"; -import { findByTestId } from "../../../test-utils"; describe("", () => { const mapOptions = { container: {} as unknown as HTMLElement, style: "" }; @@ -29,7 +26,7 @@ describe("", () => { const defaultProps = { map: mockMap, }; - const getComponent = (props = {}) => mount(); + const getComponent = (props = {}) => render(); beforeEach(() => { jest.clearAllMocks(); @@ -37,15 +34,12 @@ describe("", () => { it("renders buttons", () => { const component = getComponent(); - expect(component).toMatchSnapshot(); + expect(component.asFragment()).toMatchSnapshot(); }); it("calls map zoom in on zoom in click", () => { const component = getComponent(); - - act(() => { - findByTestId(component, "map-zoom-in-button").at(0).simulate("click"); - }); + screen.getByTestId("map-zoom-in-button").click(); expect(mockMap.zoomIn).toHaveBeenCalled(); expect(component).toBeTruthy(); @@ -53,10 +47,7 @@ describe("", () => { it("calls map zoom out on zoom out click", () => { const component = getComponent(); - - act(() => { - findByTestId(component, "map-zoom-out-button").at(0).simulate("click"); - }); + screen.getByTestId("map-zoom-out-button").click(); expect(mockMap.zoomOut).toHaveBeenCalled(); expect(component).toBeTruthy(); diff --git a/test/components/views/location/__snapshots__/LocationViewDialog-test.tsx.snap b/test/components/views/location/__snapshots__/LocationViewDialog-test.tsx.snap index a2284ceee870..32670dadac07 100644 --- a/test/components/views/location/__snapshots__/LocationViewDialog-test.tsx.snap +++ b/test/components/views/location/__snapshots__/LocationViewDialog-test.tsx.snap @@ -135,7 +135,7 @@ exports[` renders map correctly 1`] = ` > renders map correctly 1`] = ` >
renders map correctly 1`] = ` renders map correctly 1`] = ` >
renders buttons 1`] = ` - +
-
-
-
- - +
+
-
-
- + class="mx_ZoomButtons_icon" + /> +
- + `; From b62006e9157f581d563a1f13da1fd3efee63d510 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Wed, 8 Feb 2023 13:10:25 +0100 Subject: [PATCH 2/5] Checkout PR head for strict error checker --- .github/workflows/static_analysis.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/static_analysis.yaml b/.github/workflows/static_analysis.yaml index 0b8fcfa5c82e..026725b6a3b9 100644 --- a/.github/workflows/static_analysis.yaml +++ b/.github/workflows/static_analysis.yaml @@ -52,6 +52,8 @@ jobs: - "--noImplicitAny" steps: - uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.head.sha }} - name: Install Deps run: "scripts/ci/layered.sh" From 2da98a6024ffc298b731a34c00af428705e42575 Mon Sep 17 00:00:00 2001 From: Germain Date: Wed, 8 Feb 2023 12:12:14 +0000 Subject: [PATCH 3/5] Make TS strict the effects folder (#10111) --- src/effects/confetti/index.ts | 3 ++- src/effects/fireworks/index.ts | 5 +++-- src/effects/hearts/index.ts | 6 +++--- src/effects/rainfall/index.ts | 6 +++--- src/effects/snowfall/index.ts | 6 +++--- src/effects/spaceinvaders/index.ts | 6 +++--- 6 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/effects/confetti/index.ts b/src/effects/confetti/index.ts index 9900236a210f..32b6073845a5 100644 --- a/src/effects/confetti/index.ts +++ b/src/effects/confetti/index.ts @@ -1,6 +1,7 @@ /* Copyright 2020 Nurjin Jafar Copyright 2020 Nordeck IT + Consulting GmbH. + Copyright 2023 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -86,7 +87,7 @@ export default class Confetti implements ICanvasEffect { private particles: Array = []; private waveAngle = 0; - public isRunning: boolean; + public isRunning = false; public start = async (canvas: HTMLCanvasElement, timeout = 3000): Promise => { if (!canvas) { diff --git a/src/effects/fireworks/index.ts b/src/effects/fireworks/index.ts index f37b7152f0bb..40a8e7593e37 100644 --- a/src/effects/fireworks/index.ts +++ b/src/effects/fireworks/index.ts @@ -1,6 +1,7 @@ /* Copyright 2020 Nurjin Jafar Copyright 2020 Nordeck IT + Consulting GmbH. + Copyright 2023 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -69,7 +70,7 @@ export default class Fireworks implements ICanvasEffect { private context: CanvasRenderingContext2D | null = null; private supportsAnimationFrame = window.requestAnimationFrame; private particles: Array = []; - public isRunning: boolean; + public isRunning = false; public start = async (canvas: HTMLCanvasElement, timeout = 3000): Promise => { if (!canvas) { @@ -94,7 +95,7 @@ export default class Fireworks implements ICanvasEffect { if (this.particles.length < this.options.maxCount && this.isRunning) { this.createFirework(); } - const alive = []; + const alive: FireworksParticle[] = []; for (let i = 0; i < this.particles.length; i++) { if (this.move(this.particles[i])) { alive.push(this.particles[i]); diff --git a/src/effects/hearts/index.ts b/src/effects/hearts/index.ts index af4dca154fa5..3c082e35899c 100644 --- a/src/effects/hearts/index.ts +++ b/src/effects/hearts/index.ts @@ -1,5 +1,5 @@ /* - Copyright 2021 The Matrix.org Foundation C.I.C. + Copyright 2021 - 2023 The Matrix.org Foundation C.I.C. Copyright 2022 Arseny Uskov Licensed under the Apache License, Version 2.0 (the "License"); @@ -65,7 +65,7 @@ export default class Hearts implements ICanvasEffect { private context: CanvasRenderingContext2D | null = null; private particles: Array = []; - private lastAnimationTime: number; + private lastAnimationTime = 0; private colours = [ "rgba(194,210,224,1)", @@ -82,7 +82,7 @@ export default class Hearts implements ICanvasEffect { "rgba(252,116,183,1)", ]; - public isRunning: boolean; + public isRunning = false; public start = async (canvas: HTMLCanvasElement, timeout = 3000): Promise => { if (!canvas) { diff --git a/src/effects/rainfall/index.ts b/src/effects/rainfall/index.ts index c0b4d71c770c..8b91ab19b9b0 100644 --- a/src/effects/rainfall/index.ts +++ b/src/effects/rainfall/index.ts @@ -1,5 +1,5 @@ /* - Copyright 2020 The Matrix.org Foundation C.I.C. + Copyright 2020 - 2023 The Matrix.org Foundation C.I.C. Copyright 2021 Josias Allestad Licensed under the Apache License, Version 2.0 (the "License"); @@ -52,9 +52,9 @@ export default class Rainfall implements ICanvasEffect { private context: CanvasRenderingContext2D | null = null; private particles: Array = []; - private lastAnimationTime: number; + private lastAnimationTime = 0; - public isRunning: boolean; + public isRunning = false; public start = async (canvas: HTMLCanvasElement, timeout = 3000): Promise => { if (!canvas) { diff --git a/src/effects/snowfall/index.ts b/src/effects/snowfall/index.ts index 9604f7964788..8c51e8e22a93 100644 --- a/src/effects/snowfall/index.ts +++ b/src/effects/snowfall/index.ts @@ -1,5 +1,5 @@ /* - Copyright 2020 The Matrix.org Foundation C.I.C. + Copyright 2020 - 2023 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -57,9 +57,9 @@ export default class Snowfall implements ICanvasEffect { private context: CanvasRenderingContext2D | null = null; private particles: Array = []; - private lastAnimationTime: number; + private lastAnimationTime = 0; - public isRunning: boolean; + public isRunning = false; public start = async (canvas: HTMLCanvasElement, timeout = 3000): Promise => { if (!canvas) { diff --git a/src/effects/spaceinvaders/index.ts b/src/effects/spaceinvaders/index.ts index 6d148ce16d55..f375aee88b5e 100644 --- a/src/effects/spaceinvaders/index.ts +++ b/src/effects/spaceinvaders/index.ts @@ -1,5 +1,5 @@ /* - Copyright 2021 The Matrix.org Foundation C.I.C. + Copyright 2021 - 2023 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -51,9 +51,9 @@ export default class SpaceInvaders implements ICanvasEffect { private context: CanvasRenderingContext2D | null = null; private particles: Array = []; - private lastAnimationTime: number; + private lastAnimationTime = 0; - public isRunning: boolean; + public isRunning = false; public start = async (canvas: HTMLCanvasElement, timeout = 3000): Promise => { if (!canvas) { From d276e11bd1c3d75453c51b030a760b1ae8547cff Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Wed, 8 Feb 2023 15:49:09 +0100 Subject: [PATCH 4/5] Update .github/workflows/static_analysis.yaml Co-authored-by: Michael Telatynski <7t3chguy@gmail.com> --- .github/workflows/static_analysis.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/static_analysis.yaml b/.github/workflows/static_analysis.yaml index 026725b6a3b9..999f19c25899 100644 --- a/.github/workflows/static_analysis.yaml +++ b/.github/workflows/static_analysis.yaml @@ -53,7 +53,7 @@ jobs: steps: - uses: actions/checkout@v3 with: - ref: ${{ github.event.pull_request.head.sha }} + ref: ${{ github.event.pull_request.head.sha }} - name: Install Deps run: "scripts/ci/layered.sh" From f24db71753e1b0819781dbc5835adf1aac090d40 Mon Sep 17 00:00:00 2001 From: Adarsh Singh <63918341+adarsh-sgh@users.noreply.github.com> Date: Wed, 8 Feb 2023 21:59:12 +0530 Subject: [PATCH 5/5] fix: correctly identify emoticons (#10108) Signed-off-by: Adarsh Singh --- .../views/rooms/BasicMessageComposer.tsx | 9 ++- .../views/rooms/BasicMessageComposer-test.tsx | 56 ++++++++++++++----- 2 files changed, 49 insertions(+), 16 deletions(-) diff --git a/src/components/views/rooms/BasicMessageComposer.tsx b/src/components/views/rooms/BasicMessageComposer.tsx index 9f50e7b96fb4..eaf97a39f405 100644 --- a/src/components/views/rooms/BasicMessageComposer.tsx +++ b/src/components/views/rooms/BasicMessageComposer.tsx @@ -191,16 +191,19 @@ export default class BasicMessageEditor extends React.Component public replaceEmoticon(caretPosition: DocumentPosition, regex: RegExp): number { const { model } = this.props; const range = model.startRange(caretPosition); - // expand range max 8 characters backwards from caretPosition, + // expand range max 9 characters backwards from caretPosition, // as a space to look for an emoticon - let n = 8; + let n = 9; range.expandBackwardsWhile((index, offset) => { const part = model.parts[index]; n -= 1; return n >= 0 && [Type.Plain, Type.PillCandidate, Type.Newline].includes(part.type); }); const emoticonMatch = regex.exec(range.text); - if (emoticonMatch) { + // ignore matches at start of proper substrings + // so xd will not match if the string was "mixd 123456" + // and we are lookinh at xd 123456 part of the string + if (emoticonMatch && (n >= 0 || emoticonMatch.index !== 0)) { const query = emoticonMatch[1].replace("-", ""); // try both exact match and lower-case, this means that xd won't match xD but :P will match :p const data = EMOTICON_TO_EMOJI.get(query) || EMOTICON_TO_EMOJI.get(query.toLowerCase()); diff --git a/test/components/views/rooms/BasicMessageComposer-test.tsx b/test/components/views/rooms/BasicMessageComposer-test.tsx index 3ab85957cba0..34d32b627d6b 100644 --- a/test/components/views/rooms/BasicMessageComposer-test.tsx +++ b/test/components/views/rooms/BasicMessageComposer-test.tsx @@ -24,34 +24,64 @@ import * as TestUtils from "../../../test-utils"; import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; import EditorModel from "../../../../src/editor/model"; import { createPartCreator, createRenderer } from "../../../editor/mock"; +import SettingsStore from "../../../../src/settings/SettingsStore"; describe("BasicMessageComposer", () => { const renderer = createRenderer(); const pc = createPartCreator(); - beforeEach(() => { - TestUtils.stubClient(); - }); - - it("should allow a user to paste a URL without it being mangled", () => { - const model = new EditorModel([], pc, renderer); - const client: MatrixClient = MatrixClientPeg.get(); + TestUtils.stubClient(); - const roomId = "!1234567890:domain"; - const userId = client.getSafeUserId(); + const client: MatrixClient = MatrixClientPeg.get(); - const room = new Room(roomId, client, userId); + const roomId = "!1234567890:domain"; + const userId = client.getSafeUserId(); + const room = new Room(roomId, client, userId); + it("should allow a user to paste a URL without it being mangled", async () => { + const model = new EditorModel([], pc, renderer); + render(); const testUrl = "https://element.io"; const mockDataTransfer = generateMockDataTransferForString(testUrl); - - render(); - userEvent.paste(mockDataTransfer); + await userEvent.paste(mockDataTransfer); expect(model.parts).toHaveLength(1); expect(model.parts[0].text).toBe(testUrl); expect(screen.getByText(testUrl)).toBeInTheDocument(); }); + + it("should replaceEmoticons properly", async () => { + jest.spyOn(SettingsStore, "getValue").mockImplementation((settingName: string) => { + return settingName === "MessageComposerInput.autoReplaceEmoji"; + }); + userEvent.setup(); + const model = new EditorModel([], pc, renderer); + render(); + + const tranformations = [ + { before: "4:3 video", after: "4:3 video" }, + { before: "regexp 12345678", after: "regexp 12345678" }, + { before: "--:--)", after: "--:--)" }, + + { before: "we <3 matrix", after: "we ❤️ matrix" }, + { before: "hello world :-)", after: "hello world 🙂" }, + { before: ":) hello world", after: "🙂 hello world" }, + { before: ":D 4:3 video :)", after: "😄 4:3 video 🙂" }, + + { before: ":-D", after: "😄" }, + { before: ":D", after: "😄" }, + { before: ":3", after: "😽" }, + ]; + const input = screen.getByRole("textbox"); + + for (const { before, after } of tranformations) { + await userEvent.clear(input); + //add a space after the text to trigger the replacement + await userEvent.type(input, before + " "); + const transformedText = model.parts.map((part) => part.text).join(""); + expect(transformedText).toBe(after + " "); + } + }); }); function generateMockDataTransferForString(string: string): DataTransfer {