diff --git a/.github/workflows/cypress.yaml b/.github/workflows/cypress.yaml index d2df5a26ca..b1d0e4d4ff 100644 --- a/.github/workflows/cypress.yaml +++ b/.github/workflows/cypress.yaml @@ -118,8 +118,8 @@ jobs: strategy: fail-fast: false matrix: - # Naive segmentation of tests - segment: ["a-i", "j-p", "q-s", "t-z"] + # Run 4 instances in Parallel + runner: [1, 2, 3, 4] steps: - uses: browser-actions/setup-chrome@c485fa3bab6be59dce18dbc18ef6ab7cbc8ff5f1 - run: echo "BROWSER_PATH=$(which chrome)" >> $GITHUB_ENV @@ -163,7 +163,7 @@ jobs: echo "CYPRESS_RUST_CRYPTO=1" >> "$GITHUB_ENV" - name: Run Cypress tests - uses: cypress-io/github-action@fa88e4afe551e64c8827a4b9e379afc63d8f691a + uses: cypress-io/github-action@2558ee6af05072a19de2ce92cb68b38616132726 with: working-directory: matrix-react-sdk # The built-in Electron runner seems to grind to a halt trying to run the tests, so use chrome. @@ -172,11 +172,10 @@ jobs: start: npx serve -p 8080 -L ../webapp wait-on: "http://localhost:8080" record: true - parallel: false + parallel: true command-prefix: "yarn percy exec --parallel --" config: '{"reporter":"cypress-multi-reporters", "reporterOptions": { "configFile": "cypress-ci-reporter-config.json" } }' ci-build-id: ${{ needs.prepare.outputs.uuid }} - spec: cypress/e2e/[${{ matrix.segment }}]*/** env: # pass the Dashboard record key as an environment variable CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} diff --git a/.github/workflows/i18n_check.yml b/.github/workflows/i18n_check.yml index a9ff110c27..39b57028f8 100644 --- a/.github/workflows/i18n_check.yml +++ b/.github/workflows/i18n_check.yml @@ -12,7 +12,7 @@ jobs: - name: "Get modified files" id: changed_files if: github.event_name == 'pull_request' && github.event.pull_request.user.login != 'RiotTranslateBot' && github.event.pull_request.user.login != 't3chguy' - uses: tj-actions/changed-files@1c26215f3fbd51eba03bc199e5cbabdfc3584ce3 # v38 + uses: tj-actions/changed-files@48566bbcc22ceb7c5809ebdd27377309f2c3de8c # v39 with: files: | src/i18n/strings/* diff --git a/.github/workflows/netlify.yaml b/.github/workflows/netlify.yaml index 248fb50c9e..aa243d2962 100644 --- a/.github/workflows/netlify.yaml +++ b/.github/workflows/netlify.yaml @@ -41,7 +41,7 @@ jobs: - name: ☁️ Deploy to Netlify id: netlify - uses: nwtgck/actions-netlify@5da65c9f74c7961c5501a3ba329b8d0912f39c03 # v2.0 + uses: nwtgck/actions-netlify@7a92f00dde8c92a5a9e8385ec2919775f7647352 # v2.1 with: publish-dir: webapp deploy-message: "Deploy from GitHub Actions" diff --git a/.github/workflows/notify-element-web.yml b/.github/workflows/notify-element-web.yml index 39a252034c..9f88c61e8a 100644 --- a/.github/workflows/notify-element-web.yml +++ b/.github/workflows/notify-element-web.yml @@ -12,7 +12,7 @@ jobs: if: github.repository == 'matrix-org/matrix-react-sdk' steps: - name: Notify element-web repo that a new SDK build is on develop - uses: peter-evans/repository-dispatch@26b39ed245ab8f31526069329e112ab2fb224588 # v2 + uses: peter-evans/repository-dispatch@bf47d102fdb849e755b0b0023ea3e81a44b6f570 # v2 with: token: ${{ secrets.ELEMENT_BOT_TOKEN }} repository: vector-im/element-web diff --git a/CHANGELOG.md b/CHANGELOG.md index b588020eb7..7fcd0a52e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,39 @@ +Changes in [3.80.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.80.1) (2023-09-13) +===================================================================================================== + +## 🐛 Bug Fixes + * Update Compound to fix Firefox-specific avatar regression ([\#11604](https://github.com/matrix-org/matrix-react-sdk/pull/11604)) + +Changes in [3.80.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.80.0) (2023-09-12) +===================================================================================================== + +## ✨ Features + * Allow creating public knock rooms ([\#11481](https://github.com/matrix-org/matrix-react-sdk/pull/11481)). Contributed by @charlynguyen. + * Render custom images in reactions according to MSC4027 ([\#11087](https://github.com/matrix-org/matrix-react-sdk/pull/11087)). Contributed by @sumnerevans. + * Introduce room knocks bar ([\#11475](https://github.com/matrix-org/matrix-react-sdk/pull/11475)). Contributed by @charlynguyen. + * Room header UI updates ([\#11507](https://github.com/matrix-org/matrix-react-sdk/pull/11507)). Fixes vector-im/element-web#25892. + * Remove green "verified" bar for encrypted events ([\#11496](https://github.com/matrix-org/matrix-react-sdk/pull/11496)). + * Update member count on room summary update ([\#11488](https://github.com/matrix-org/matrix-react-sdk/pull/11488)). + * Support for E2EE in Element Call ([\#11492](https://github.com/matrix-org/matrix-react-sdk/pull/11492)). + * Allow requesting to join knock rooms via spotlight ([\#11482](https://github.com/matrix-org/matrix-react-sdk/pull/11482)). Contributed by @charlynguyen. + * Lock out the first tab if Element is opened in a second tab. ([\#11425](https://github.com/matrix-org/matrix-react-sdk/pull/11425)). Fixes vector-im/element-web#25157. + * Change avatar to use Compound implementation ([\#11448](https://github.com/matrix-org/matrix-react-sdk/pull/11448)). + +## 🐛 Bug Fixes + * Fix vertical alignment of default avatar font ([\#11582](https://github.com/matrix-org/matrix-react-sdk/pull/11582)). Fixes vector-im/element-web#26081. + * Fix avatars in public room & space search being flex shrunk ([\#11580](https://github.com/matrix-org/matrix-react-sdk/pull/11580)). Fixes vector-im/element-web#26133. + * Fix EventTile avatars being rendered with a size of 0 instead of hidden ([\#11558](https://github.com/matrix-org/matrix-react-sdk/pull/11558)). Fixes vector-im/element-web#26075. + * Use RoomStateEvent.Update for knocks ([\#11516](https://github.com/matrix-org/matrix-react-sdk/pull/11516)). Contributed by @charlynguyen. + * Prevent event propagation when clicking icon buttons ([\#11515](https://github.com/matrix-org/matrix-react-sdk/pull/11515)). + * Only display RoomKnocksBar when feature flag is enabled ([\#11513](https://github.com/matrix-org/matrix-react-sdk/pull/11513)). Contributed by @andybalaam. + * Fix avatars of knock members for people tab of room settings ([\#11506](https://github.com/matrix-org/matrix-react-sdk/pull/11506)). Fixes vector-im/element-web#26083. Contributed by @charlynguyen. + * Fixes read receipt avatar offset ([\#11483](https://github.com/matrix-org/matrix-react-sdk/pull/11483)). Fixes vector-im/element-web#26067, vector-im/element-web#26064 vector-im/element-web#26059 and vector-im/element-web#26061. + * Fix avatar defects ([\#11473](https://github.com/matrix-org/matrix-react-sdk/pull/11473)). Fixes vector-im/element-web#26051 and vector-im/element-web#26046. + * Fix consistent avatar output for Percy ([\#11472](https://github.com/matrix-org/matrix-react-sdk/pull/11472)). Fixes vector-im/element-web#26049 and vector-im/element-web#26052. + * Fix colour of avatar and colour matching with username ([\#11470](https://github.com/matrix-org/matrix-react-sdk/pull/11470)). Fixes vector-im/element-web#26042. + * Fix incompatibility of Soft Logout with Element-R ([\#11468](https://github.com/matrix-org/matrix-react-sdk/pull/11468)). + * Fix instances of double translation and guard translation calls using typescript ([\#11443](https://github.com/matrix-org/matrix-react-sdk/pull/11443)). + Changes in [3.79.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.79.0) (2023-08-29) ===================================================================================================== diff --git a/cypress.config.ts b/cypress.config.ts index bc24763852..c56f0e7097 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -15,9 +15,10 @@ limitations under the License. */ import { defineConfig } from "cypress"; +import * as fs from "node:fs"; export default defineConfig({ - videoUploadOnPasses: false, + video: true, projectId: "ppvnzg", experimentalInteractiveRunEvents: true, experimentalMemoryManagement: true, @@ -25,6 +26,18 @@ export default defineConfig({ chromeWebSecurity: false, e2e: { setupNodeEvents(on, config) { + // Delete videos of passing tests + on("after:spec", (spec, results) => { + if (results && results.video) { + const failures = results.tests.some((test) => + test.attempts.some((attempt) => attempt.state === "failed"), + ); + if (!failures) { + fs.unlinkSync(results.video); + } + } + }); + return require("./cypress/plugins/index.ts").default(on, config); }, baseUrl: "http://localhost:8080", diff --git a/cypress/e2e/read-receipts/high-level.spec.ts b/cypress/e2e/read-receipts/high-level.spec.ts index a49804a38d..63a24edcf7 100644 --- a/cypress/e2e/read-receipts/high-level.spec.ts +++ b/cypress/e2e/read-receipts/high-level.spec.ts @@ -103,6 +103,29 @@ describe("Read receipts", () => { }); } + function backToThreadsList() { + cy.log("Back to threads list"); + cy.get(".mx_RightPanel").findByTitle("Threads").click(); + } + + /** + * Find and display a message. + * + * @param room the name of the room to look inside + * @param message the content of the message to fine + * @param includeThreads look for messages inside threads, not just the main timeline + */ + function jumpTo(room: string, message: string, includeThreads = false) { + cy.log("Jump to message", room, message, includeThreads); + cy.getClient().then((cli) => { + findRoomByName(room).then(async ({ roomId }) => { + const roomObject = cli.getRoom(roomId); + const foundMessage = await getMessage(roomObject, message, includeThreads); + cy.visit(`/#/room/${roomId}/${foundMessage.getId()}`); + }); + }); + } + function openThread(rootMessage: string) { cy.log("Open thread", rootMessage); cy.get(".mx_RoomView_body", { log: false }).within(() => { @@ -246,6 +269,26 @@ describe("Read receipts", () => { })(); } + /** + * Generate MessageContentSpecs to send multiple threaded responses into a room. + * + * @param rootMessage - the body of the thread root message to send a response to + * @param newMessages - the contents of the messages + */ + function manyThreadedOff(rootMessage: string, newMessages: Array): Array { + return newMessages.map((body) => threadedOff(rootMessage, body)); + } + + /** + * Generate strings with the supplied prefix, suffixed with numbers. + * + * @param prefix the prefix of each string + * @param howMany the number of strings to generate + */ + function many(prefix: string, howMany: number): Array { + return Array.from(Array(howMany).keys()).map((i) => prefix + i.toFixed()); + } + /** * BotActionSpec to send a reaction to an existing event into a room * @param targetMessage - the body of the message to send a reaction to @@ -311,6 +354,16 @@ describe("Read receipts", () => { }); } + /** + * Assert that this room remains read, when it was previously read. + * (In practice, this just waits a short while to allow any unread marker to + * appear, and then asserts that the room is read.) + */ + function assertStillRead(room: string) { + cy.wait(200); + assertRead(room); + } + /** * Assert a given room is marked as unread (via the room list tile) * @param room - the name of the room to check @@ -327,8 +380,24 @@ describe("Read receipts", () => { }); } + /** + * Assert a given room is marked as unread, and the number of unread + * messages is less than the supplied count. + * + * @param room - the name of the room to check + * @param lessThan - the number of unread messages that is too many + */ + function assertUnreadLessThan(room: string, lessThan: number) { + cy.log("Assert room some unread", room); + return getRoomListTile(room).within(() => { + cy.get(".mx_NotificationBadge_count").should(($count) => + expect(parseInt($count.get(0).textContent, 10)).to.be.lessThan(lessThan), + ); + }); + } + function openThreadList() { - cy.log("Open thread list"); + cy.log("Open threads list"); cy.findByTestId("threadsButton", { log: false }).then(($button) => { if ($button?.attr("aria-current") !== "true") { cy.findByTestId("threadsButton", { log: false }).click(); @@ -405,7 +474,19 @@ describe("Read receipts", () => { // Then the room becomes read assertRead(room2); }); - it.skip("Reading an older message leaves the room unread", () => {}); + // XXX: fails (sometimes!) because the unread count stays high + it.skip("Reading an older message leaves the room unread", () => { + // Given there are lots of messages in a room + goTo(room1); + receiveMessages(room2, many("Msg", 30)); + assertUnread(room2, 30); + + // When I jump to one of the older messages + jumpTo(room2, "Msg1"); + + // Then the room is still unread, but some messages were read + assertUnreadLessThan(room2, 30); + }); it("Marking a room as read makes it read", () => { // Given I have some unread messages goTo(room1); @@ -447,7 +528,21 @@ describe("Read receipts", () => { // Then I still have an unread message assertUnread(room2, 1); }); - it.skip("A room where all messages are read is still read after restart", () => {}); + it("A room where all messages are read is still read after restart", () => { + // Given I have read all messages + goTo(room1); + assertRead(room2); + receiveMessages(room2, ["Msg1"]); + assertUnread(room2, 1); + goTo(room2); + assertRead(room2); + + // When I restart + saveAndReload(); + + // Then all messages are still read + assertRead(room2); + }); it("A room that was marked as read is still read after restart", () => { // Given I have marked all messages as read goTo(room1); @@ -525,7 +620,25 @@ describe("Read receipts", () => { assertReadThread("Msg1"); assertRead(room2); }); - it.skip("Reading an older thread message (via permalink) leaves the thread unread", () => {}); + it("Reading an older thread message leaves the thread unread", () => { + // Given there are many messages in a thread + goTo(room1); + receiveMessages(room2, ["ThreadRoot", ...manyThreadedOff("ThreadRoot", many("InThread", 20))]); + assertUnread(room2, 21); + + // When I read an older message in the thread + jumpTo(room2, "InThread1", true); + assertUnreadLessThan(room2, 21); + // TODO: for some reason, we can't find the first message + // "InThread0", so I am using the second here. Also, they appear + // out of order, with "InThread2" before "InThread1". Might be a + // clue to the sporadic reports we have had of messages going + // missing in threads? + + // Then the thread is still marked as unread + backToThreadsList(); + assertUnreadThread("ThreadRoot"); + }); it("Reading only one thread's message does not make the room read", () => { // Given two threads are unread goTo(room1); @@ -668,23 +781,56 @@ describe("Read receipts", () => { assertUnread(room2, 1); assertUnreadThread("Msg1"); }); - it.skip("Reading a thread root within the thread view marks it as read in the main timeline", () => {}); - it("Creating a new thread based on a reply makes the room unread", () => { + // XXX: fails because we jump to the wrong place in the timeline + it.skip("Reading a thread root within the thread view marks it as read in the main timeline", () => { + // Given lots of messages are on the main timeline, and one has a thread off it goTo(room1); - receiveMessages(room2, ["Msg1", replyTo("Msg1", "Reply1"), threadedOff("Reply1", "Resp1")]); - assertUnread(room2, 3); + receiveMessages(room2, [ + ...many("beforeThread", 30), + "ThreadRoot", + threadedOff("ThreadRoot", "InThread"), + ...many("afterThread", 30), + ]); + assertUnread(room2, 62); // Sanity + + // When I jump to an old message and read the thread + jumpTo(room2, "beforeThread0"); + openThread("ThreadRoot"); + + // Then the thread root is marked as read in the main timeline, + // so there are only 30 left - the ones after the thread root. + assertUnread(room2, 30); + }); + it("Creating a new thread based on a reply makes the room unread", () => { + // Given a message and reply exist and are read + goTo(room1); + receiveMessages(room2, ["Msg1", replyTo("Msg1", "Reply1")]); + goTo(room2); + assertRead(room2); + goTo(room1); + assertRead(room2); + + // When I receive a thread message created on the reply + receiveMessages(room2, [threadedOff("Reply1", "Resp1")]); + + // Then the room is unread + assertUnread(room2, 1); }); it("Reading a thread whose root is a reply makes the room read", () => { + // Given an unread thread off a reply exists goTo(room1); receiveMessages(room2, ["Msg1", replyTo("Msg1", "Reply1"), threadedOff("Reply1", "Resp1")]); assertUnread(room2, 3); - goTo(room2); assertUnread(room2, 1); assertUnreadThread("Reply1"); + // When I read the thread openThread("Reply1"); + + // Then the room and thread are read assertRead(room2); + assertReadThread("Reply1"); }); }); }); @@ -692,15 +838,16 @@ describe("Read receipts", () => { describe("editing messages", () => { describe("in the main timeline", () => { // TODO: this passes but we think this should fail, because we think edits should not cause unreads. - // XXX: fails because on CI we get a dot, but locally we get a count. Must be a timing issue. + // XXX: fails because we see a dot instead of an unread number - probably the server and client disagree it.skip("Editing a message makes a room unread", () => { // Given I am not looking at the room goTo(room1); receiveMessages(room2, ["Msg1"]); assertUnread(room2, 1); - markAsRead(room2); + goTo(room2); assertRead(room2); + goTo(room1); // When an edit appears in the room receiveMessages(room2, [editOf("Msg1", "Msg1 Edit1")]); @@ -708,7 +855,7 @@ describe("Read receipts", () => { // Then it becomes unread assertUnread(room2, 1); }); - // XXX: fails because on CI we get a dot, but locally we get a count. Must be a timing issue. + // XXX: fails because we see a dot instead of an unread number - probably the server and client disagree it.skip("Reading an edit makes the room read", () => { // Given an edit is making the room unread goTo(room1); @@ -730,14 +877,12 @@ describe("Read receipts", () => { goTo(room1); assertRead(room2); }); - // XXX: fails because on CI we get a dot, but locally we get a count. Must be a timing issue. - it.skip("Marking a room as read after an edit makes it read", () => { - // Given an edit is makng a room unread - goTo(room1); + it("Marking a room as read after an edit makes it read", () => { + // Given an edit is making a room unread + goTo(room2); receiveMessages(room2, ["Msg1"]); - assertUnread(room2, 1); - markAsRead(room2); assertRead(room2); + goTo(room1); receiveMessages(room2, [editOf("Msg1", "Msg1 Edit1")]); assertUnread(room2, 1); @@ -747,7 +892,7 @@ describe("Read receipts", () => { // Then the room becomes read assertRead(room2); }); - // XXX: fails because on CI we get a dot, but locally we get a count. Must be a timing issue. + // XXX: fails because we see a dot instead of an unread number - probably the server and client disagree it.skip("Editing a message after marking as read makes the room unread", () => { // Given the room is marked as read goTo(room1); @@ -762,7 +907,7 @@ describe("Read receipts", () => { // Then the room becomes unread assertUnread(room2, 1); }); - // XXX: fails because on CI we get a dot, but locally we get a count. Must be a timing issue. + // XXX: fails because we see a dot instead of an unread number - probably the server and client disagree it.skip("Editing a reply after reading it makes the room unread", () => { // Given the room is all read goTo(room1); @@ -780,7 +925,7 @@ describe("Read receipts", () => { // Then it becomes unread assertUnread(room2, 1); }); - // XXX: fails because on CI we get a dot, but locally we get a count. Must be a timing issue. + // XXX: fails because we see a dot instead of an unread number - probably the server and client disagree it.skip("Editing a reply after marking as read makes the room unread", () => { // Given a reply is marked as read goTo(room1); @@ -795,14 +940,13 @@ describe("Read receipts", () => { // Then the room becomes unread assertUnread(room2, 1); }); - // XXX: fails because on CI we get a dot, but locally we get a count. Must be a timing issue. + // XXX: fails because we see a dot instead of an unread number - probably the server and client disagree it.skip("A room with an edit is still unread after restart", () => { // Given a message is marked as read - goTo(room1); + goTo(room2); receiveMessages(room2, ["Msg1"]); - assertUnread(room2, 1); - markAsRead(room2); assertRead(room2); + goTo(room1); // When an edit appears in the room receiveMessages(room2, [editOf("Msg1", "Msg1 Edit1")]); @@ -814,13 +958,22 @@ describe("Read receipts", () => { saveAndReload(); assertUnread(room2, 1); }); - // XXX: fails because on CI we get a dot, but locally we get a count. Must be a timing issue. - it.skip("A room where all edits are read is still read after restart", () => { - // Given an edit made the room unread - goTo(room1); + it("An edited message becomes read if it happens while I am looking", () => { + // Given a message is marked as read + goTo(room2); + receiveMessages(room2, ["Msg1"]); + assertRead(room2); + + // When I see an edit appear in the room I am looking at + receiveMessages(room2, [editOf("Msg1", "Msg1 Edit1")]); + + // Then it becomes read + assertRead(room2); + }); + it("A room where all edits are read is still read after restart", () => { + // Given an edit made the room unread + goTo(room2); receiveMessages(room2, ["Msg1"]); - assertUnread(room2, 1); - markAsRead(room2); assertRead(room2); receiveMessages(room2, [editOf("Msg1", "Msg1 Edit1")]); assertUnread(room2, 1); @@ -838,40 +991,50 @@ describe("Read receipts", () => { }); describe("in threads", () => { - // XXX: fails because on CI we get a dot, but locally we get a count. Must be a timing issue. + // XXX: fails because we see a dot instead of an unread number - probably the server and client disagree it.skip("An edit of a threaded message makes the room unread", () => { + // Given we have read the thread goTo(room1); receiveMessages(room2, ["Msg1", threadedOff("Msg1", "Resp1")]); assertUnread(room2, 2); - goTo(room2); openThread("Msg1"); assertRead(room2); + backToThreadsList(); goTo(room1); + // When a message inside it is edited receiveMessages(room2, [editOf("Resp1", "Edit1")]); + + // Then the room and thread are unread assertUnread(room2, 1); + goTo(room2); + assertUnreadThread("Msg1"); }); - // XXX: fails because on CI we get a dot, but locally we get a count. Must be a timing issue. + // XXX: fails because we see a dot instead of an unread number - probably the server and client disagree it.skip("Reading an edit of a threaded message makes the room read", () => { + // Given an edited thread message is making the room unread goTo(room1); receiveMessages(room2, ["Msg1", threadedOff("Msg1", "Resp1")]); assertUnread(room2, 2); - goTo(room2); openThread("Msg1"); assertRead(room2); goTo(room1); - receiveMessages(room2, [editOf("Resp1", "Edit1")]); assertUnread(room2, 1); + // When I read it goTo(room2); openThread("Msg1"); + + // Then the room and thread are read assertRead(room2); + assertReadThread("Msg1"); }); // XXX: fails because the room is still "bold" even though the notification counts all disappear it.skip("Marking a room as read after an edit in a thread makes it read", () => { + // Given an edit in a thread is making the room unread goTo(room1); receiveMessages(room2, ["Msg1", threadedOff("Msg1", "Resp1"), editOf("Resp1", "Edit1")]); assertUnread(room2, 3); // TODO: the edit counts as a message! @@ -882,7 +1045,7 @@ describe("Read receipts", () => { // Then it is read assertRead(room2); }); - // XXX: fails because the room is still "bold" even though the notification counts all disappear + // XXX: fails because the unread dot remains after marking as read it.skip("Editing a thread message after marking as read makes the room unread", () => { // Given a room is marked as read goTo(room1); @@ -897,49 +1060,76 @@ describe("Read receipts", () => { // Then the room becomes unread assertUnread(room2, 1); // TODO: should this edit make us unread? }); - // XXX: fails because on CI the count is 2 instead of 3. Must be a timing issue. + // XXX: fails because we see a dot instead of an unread number - probably the server and client disagree it.skip("A room with an edited threaded message is still unread after restart", () => { + // Given an edit in a thread is making a room unread goTo(room1); - receiveMessages(room2, ["Msg1", threadedOff("Msg1", "Resp1"), editOf("Resp1", "Edit1")]); - assertUnread(room2, 3); - - saveAndReload(); - assertUnread(room2, 3); - }); - // XXX: fails because on CI the count is 2 instead of 3. Must be a timing issue. - it.skip("A room where all threaded edits are read is still read after restart", () => { - goTo(room1); - receiveMessages(room2, ["Msg1", threadedOff("Msg1", "Resp1"), editOf("Resp1", "Edit1")]); - assertUnread(room2, 3); - + receiveMessages(room2, ["Msg1", threadedOff("Msg1", "Resp1")]); markAsRead(room2); + receiveMessages(room2, [editOf("Resp1", "Edit1")]); + assertUnread(room2, 1); + + // When I restart + saveAndReload(); + + // Then is it still unread + assertUnread(room2, 1); + }); + it("A room where all threaded edits are read is still read after restart", () => { + goTo(room2); + receiveMessages(room2, ["Msg1", threadedOff("Msg1", "Resp1"), editOf("Resp1", "Edit1")]); + assertUnread(room2, 2); + openThread("Msg1"); + assertRead(room2); + goTo(room1); // Make sure we are looking at room1 after reload assertRead(room2); saveAndReload(); assertRead(room2); }); + // XXX: fails because we see a dot instead of an unread number - probably the server and client disagree + it.skip("A room where all threaded edits are marked as read is still read after restart", () => { + goTo(room1); + receiveMessages(room2, ["Msg1", threadedOff("Msg1", "Resp1"), editOf("Resp1", "Edit1")]); + assertUnread(room2, 3); + markAsRead(room2); + assertRead(room2); + + // When I restart + saveAndReload(); + + // It is still read + assertRead(room2); + }); }); describe("thread roots", () => { - // XXX: fails because on CI we get a dot, but locally we get a count. Must be a timing issue. + // XXX: fails because we see a dot instead of an unread number - probably the server and client disagree it.skip("An edit of a thread root makes the room unread", () => { + // Given I have read a thread goTo(room1); receiveMessages(room2, ["Msg1", threadedOff("Msg1", "Resp1")]); assertUnread(room2, 2); - goTo(room2); openThread("Msg1"); assertRead(room2); goTo(room1); + // When the thread root is edited receiveMessages(room2, [editOf("Msg1", "Edit1")]); + + // Then the room is unread but not the thread assertUnread(room2, 1); + goTo(room2); + assertRead(room2); + assertReadThread("Msg1"); }); - it.skip("Reading an edit of a thread root makes the room read", () => { + it("Reading an edit of a thread root makes the room read", () => { // Given a fully-read thread exists goTo(room2); receiveMessages(room2, ["Msg1", threadedOff("Msg1", "Resp1")]); openThread("Msg1"); + assertRead(room2); goTo(room1); assertRead(room2); @@ -954,10 +1144,77 @@ describe("Read receipts", () => { goTo(room1); assertRead(room2); }); - it.skip("Marking a room as read after an edit of a thread root makes it read", () => {}); - it.skip("Editing a thread root after marking as read makes the room unread", () => {}); - it.skip("Marking a room as read after an edit of a thread root that is a reply makes it read", () => {}); - it.skip("Editing a thread root that is a reply after marking as read makes the room unread but not the thread", () => {}); + // XXX: fails because it shows a dot instead of unread count + it.skip("Editing a thread root after reading makes the room unread", () => { + // Given a fully-read thread exists + goTo(room2); + receiveMessages(room2, ["Msg1", threadedOff("Msg1", "Resp1")]); + openThread("Msg1"); + assertRead(room2); + goTo(room1); + + // When the thread root is edited + receiveMessages(room2, [editOf("Msg1", "Msg1 Edit1")]); + + // Then the room becomes unread + assertUnread(room2, 1); + }); + // XXX: fails because the room has an unread dot after I marked it as read + it.skip("Marking a room as read after an edit of a thread root makes it read", () => { + // Given a fully-read thread exists + goTo(room2); + receiveMessages(room2, ["Msg1", threadedOff("Msg1", "Resp1")]); + openThread("Msg1"); + assertRead(room2); + goTo(room1); + assertRead(room2); + + // When the thread root is edited + receiveMessages(room2, [editOf("Msg1", "Msg1 Edit1")]); + + // And I mark the room as read + markAsRead(room2); + + // Then the room becomes read and stays read + assertRead(room2); + goTo(room1); + assertRead(room2); + }); + // XXX: fails because the room has an unread dot after I marked it as read + it.skip("Editing a thread root that is a reply after marking as read makes the room unread but not the thread", () => { + // Given a thread based on a reply exists and is read because it is marked as read + goTo(room1); + receiveMessages(room2, ["Msg", replyTo("Msg", "Reply"), threadedOff("Reply", "InThread")]); + assertUnread(room2, 3); + markAsRead(room2); + assertRead(room2); + + // When I edit the thread root + receiveMessages(room1, [editOf("Reply", "Edited Reply")]); + + // Then the room is unread + assertUnread(room2, 1); + goTo(room2); + + // But the thread is still read (because the root is not part of the thread) + assertReadThread("EditedReply"); + }); + // XXX: fails because the room has an unread dot after I marked it as read + it.skip("Marking a room as read after an edit of a thread root that is a reply makes it read", () => { + // Given a thread based on a reply exists and the reply has been edited + goTo(room1); + receiveMessages(room2, ["Msg", replyTo("Msg", "Reply"), threadedOff("Reply", "InThread")]); + receiveMessages(room2, [editOf("Reply", "Edited Reply")]); + assertUnread(room2, 3); + + // When I mark the room as read + markAsRead(room2); + + // Then the room and thread are read + assertRead(room2); + goTo(room2); + assertReadThread("Edited Reply"); + }); }); }); @@ -1042,57 +1299,318 @@ describe("Read receipts", () => { describe("thread roots", () => { it("A reaction to a thread root does not make the room unread", () => { + // Given a read thread root exists + goTo(room1); + assertRead(room2); + receiveMessages(room2, ["Msg1", threadedOff("Msg1", "Reply1")]); + assertUnread(room2, 2); + goTo(room2); + openThread("Msg1"); + assertRead(room2); + + // When someone reacts to it + goTo(room1); + receiveMessages(room2, [reactionTo("Msg1", "🪿")]); + cy.wait(200); + + // Then the room is still read + assertRead(room2); + }); + it("Reading a reaction to a thread root leaves the room read", () => { + // Given a read thread root exists + goTo(room1); + assertRead(room2); + receiveMessages(room2, ["Msg1", threadedOff("Msg1", "Reply1")]); + assertUnread(room2, 2); + goTo(room2); + openThread("Msg1"); + assertRead(room2); + + // And the reaction to it does not make us unread + goTo(room1); + receiveMessages(room2, [reactionTo("Msg1", "🪿")]); + assertRead(room2); + + // When we read the reaction and go away again + goTo(room2); + openThread("Msg1"); + assertRead(room2); + goTo(room1); + cy.wait(200); + + // Then the room is still read + assertRead(room2); + }); + // XXX: fails because the room is still "bold" even though the notification counts all disappear + it.skip("Reacting to a thread root after marking as read makes the room unread but not the thread", () => { + // Given a thread root exists goTo(room1); assertRead(room2); receiveMessages(room2, ["Msg1", threadedOff("Msg1", "Reply1")]); assertUnread(room2, 2); - goTo(room2); - openThread("Msg1"); + // And we have marked the room as read + markAsRead(room2); assertRead(room2); - goTo(room1); + // When someone reacts to it receiveMessages(room2, [reactionTo("Msg1", "🪿")]); + cy.wait(200); + // Then the room is still read assertRead(room2); }); - it.skip("Reading a reaction to a thread root makes the room read", () => {}); - it.skip("Marking a room as read after a reaction to a thread root makes it read", () => {}); - it.skip("Reacting to a thread root after marking as read makes the room unread but not the thread", () => {}); }); }); describe("redactions", () => { describe("in the main timeline", () => { it("Redacting the message pointed to by my receipt leaves the room read", () => { + // Given I have read the messages in a room goTo(room1); + receiveMessages(room2, ["Msg1", "Msg2"]); + assertUnread(room2, 2); + goTo(room2); assertRead(room2); + goTo(room1); + + // When the latest message is redacted + receiveMessages(room2, [redactionOf("Msg2")]); + + // Then the room remains read + assertStillRead(room2); + }); + + it("Reading an unread room after a redaction of the latest message makes it read", () => { + // Given an unread room + goTo(room1); receiveMessages(room2, ["Msg1", "Msg2"]); assertUnread(room2, 2); - // When I read the main timeline + // And the latest message has been redacted + receiveMessages(room2, [redactionOf("Msg2")]); + + // When I read the room + goTo(room2); + assertRead(room2); + goTo(room1); + + // Then it becomes read + assertStillRead(room2); + }); + it("Reading an unread room after a redaction of an older message makes it read", () => { + // Given an unread room with an earlier redaction + goTo(room1); + receiveMessages(room2, ["Msg1", "Msg2"]); + assertUnread(room2, 2); + receiveMessages(room2, [redactionOf("Msg1")]); + + // When I read the room + goTo(room2); + assertRead(room2); + goTo(room1); + + // Then it becomes read + assertStillRead(room2); + }); + it("Marking an unread room as read after a redaction makes it read", () => { + // Given an unread room where latest message is redacted + goTo(room1); + receiveMessages(room2, ["Msg1", "Msg2"]); + assertUnread(room2, 2); + receiveMessages(room2, [redactionOf("Msg2")]); + assertUnread(room2, 1); + + // When I mark it as read + markAsRead(room2); + + // Then it becomes read + assertRead(room2); + }); + it("Sending and redacting a message after marking the room as read makes it read", () => { + // Given a room that is marked as read + goTo(room1); + receiveMessages(room2, ["Msg1", "Msg2"]); + assertUnread(room2, 2); + markAsRead(room2); + assertRead(room2); + + // When a message is sent and then redacted + receiveMessages(room2, ["Msg3"]); + assertUnread(room2, 1); + receiveMessages(room2, [redactionOf("Msg3")]); + + // Then the room is read + assertRead(room2); + }); + it("Redacting a message after marking the room as read leaves it read", () => { + // Given a room that is marked as read + goTo(room1); + receiveMessages(room2, ["Msg1", "Msg2", "Msg3"]); + assertUnread(room2, 3); + markAsRead(room2); + assertRead(room2); + + // When we redact some messages + receiveMessages(room2, [redactionOf("Msg3")]); + receiveMessages(room2, [redactionOf("Msg1")]); + + // Then it is still read + assertStillRead(room2); + }); + it("Redacting one of the unread messages reduces the unread count", () => { + // Given an unread room + goTo(room1); + receiveMessages(room2, ["Msg1", "Msg2", "Msg3"]); + assertUnread(room2, 3); + + // When I redact a non-latest message + receiveMessages(room2, [redactionOf("Msg2")]); + + // Then the unread count goes down + assertUnread(room2, 2); + + // And when I redact the latest message + receiveMessages(room2, [redactionOf("Msg3")]); + + // Then the unread count goes down again + assertUnread(room2, 1); + }); + it("Redacting one of the unread messages reduces the unread count after restart", () => { + // Given unread count was reduced by redacting messages + goTo(room1); + receiveMessages(room2, ["Msg1", "Msg2", "Msg3"]); + assertUnread(room2, 3); + receiveMessages(room2, [redactionOf("Msg2")]); + assertUnread(room2, 2); + receiveMessages(room2, [redactionOf("Msg3")]); + assertUnread(room2, 1); + + // When I restart + saveAndReload(); + + // Then the unread count is still reduced + assertUnread(room2, 1); + }); + it("Redacting all unread messages makes the room read", () => { + // Given an unread room + goTo(room1); + receiveMessages(room2, ["Msg1", "Msg2"]); + assertUnread(room2, 2); + + // When I redact all the unread messages + receiveMessages(room2, [redactionOf("Msg2")]); + receiveMessages(room2, [redactionOf("Msg1")]); + + // Then the room is back to being read + assertRead(room2); + }); + it("Redacting all unread messages makes the room read after restart", () => { + // Given all unread messages were redacted + goTo(room1); + receiveMessages(room2, ["Msg1", "Msg2"]); + assertUnread(room2, 2); + receiveMessages(room2, [redactionOf("Msg2")]); + receiveMessages(room2, [redactionOf("Msg1")]); + assertRead(room2); + + // When I restart + saveAndReload(); + + // Then the room is still read + assertRead(room2); + }); + // TODO: Doesn't work because the test setup can't (yet) find the ID of a redacted message + it.skip("Reacting to a redacted message leaves the room read", () => { + // Given a redacted message exists + goTo(room1); + receiveMessages(room2, ["Msg1", "Msg2"]); + receiveMessages(room2, [redactionOf("Msg2")]); + assertUnread(room2, 1); + + // And the room is read + goTo(room2); + assertRead(room2); + cy.wait(200); + goTo(room1); + + // When I react to the redacted message + // TODO: doesn't work yet because we need to be able to look up + // the ID of Msg2 even though it has now disappeared from the + // timeline. + receiveMessages(room2, [reactionTo("Msg2", "🪿")]); + + // Then the room is still read + assertStillRead(room2); + }); + // TODO: Doesn't work because the test setup can't (yet) find the ID of a redacted message + it.skip("Editing a redacted message leaves the room read", () => { + // Given a redacted message exists + goTo(room1); + receiveMessages(room2, ["Msg1", "Msg2"]); + receiveMessages(room2, [redactionOf("Msg2")]); + assertUnread(room2, 1); + + // And the room is read + goTo(room2); + assertRead(room2); + goTo(room1); + + // When I attempt to edit the redacted message + // TODO: doesn't work yet because we need to be able to look up + // the ID of Msg2 even though it has now disappeared from the + // timeline. + receiveMessages(room2, [editOf("Msg2", "Msg2 is BACK")]); + + // Then the room is still read + assertStillRead(room2); + }); + // TODO: Doesn't work because the test setup can't (yet) find the ID of a redacted message + it.skip("A reply to a redacted message makes the room unread", () => { + // Given a message was redacted + goTo(room1); + receiveMessages(room2, ["Msg1", "Msg2"]); + receiveMessages(room2, [redactionOf("Msg2")]); + assertUnread(room2, 1); + + // And the room is read + goTo(room2); + assertRead(room2); + goTo(room1); + + // When I receive a reply to the redacted message + // TODO: doesn't work yet because we need to be able to look up + // the ID of Msg2 even though it has now disappeared from the + // timeline. + receiveMessages(room2, [replyTo("Msg2", "Reply to Msg2")]); + + // Then the room is unread + assertUnread(room2, 1); + }); + // TODO: Doesn't work because the test setup can't (yet) find the ID of a redacted message + it.skip("Reading a reply to a redacted message marks the room as read", () => { + // Given someone replied to a redacted message + goTo(room1); + receiveMessages(room2, ["Msg1", "Msg2"]); + receiveMessages(room2, [redactionOf("Msg2")]); + assertUnread(room2, 1); + goTo(room2); + assertRead(room2); + goTo(room1); + // TODO: doesn't work yet because we need to be able to look up + // the ID of Msg2 even though it has now disappeared from the + // timeline. + receiveMessages(room2, [replyTo("Msg2", "Reply to Msg2")]); + assertUnread(room2, 1); + + // When I read the reply goTo(room2); assertRead(room2); + // Then the room is unread goTo(room1); - receiveMessages(room2, [redactionOf("Msg2")]); - assertRead(room2); + assertStillRead(room2); }); - - it.skip("Reading an unread room after a redaction of the latest message makes it read", () => {}); - it.skip("Reading an unread room after a redaction of an older message makes it read", () => {}); - it.skip("Marking an unread room as read after a redaction makes it read", () => {}); - it.skip("Sending and redacting a message after marking the room as read makes it unread", () => {}); - it.skip("?? Redacting a message after marking the room as read makes it unread", () => {}); - it.skip("Reacting to a redacted message leaves the room read", () => {}); - it.skip("Editing a redacted message leaves the room read", () => {}); - - it.skip("?? Reading a reaction to a redacted message marks the room as read", () => {}); - it.skip("?? Reading an edit of a redacted message marks the room as read", () => {}); - it.skip("Reading a reply to a redacted message marks the room as read", () => {}); - - it.skip("A room with an unread redaction is still unread after restart", () => {}); - it.skip("A room with a read redaction is still read after restart", () => {}); }); describe("in threads", () => { diff --git a/cypress/e2e/spotlight/spotlight.spec.ts b/cypress/e2e/spotlight/spotlight.spec.ts index d7ce14eff9..0e8ad33672 100644 --- a/cypress/e2e/spotlight/spotlight.spec.ts +++ b/cypress/e2e/spotlight/spotlight.spec.ts @@ -226,8 +226,8 @@ describe("Spotlight", () => { cy.get(".mx_SpotlightDialog_filter").should("contain", "Public spaces"); cy.spotlightSearch().type("{backspace}"); cy.get(".mx_SpotlightDialog_filter").should("not.exist"); + cy.wait(200); // Again, wait to settle so keypresses arrive correctly - cy.spotlightSearch().type("{downArrow}"); cy.spotlightSearch().type("{downArrow}"); cy.get("#mx_SpotlightDialog_button_explorePublicRooms").should("have.attr", "aria-selected", "true"); cy.spotlightSearch().type("{enter}"); @@ -240,6 +240,7 @@ describe("Spotlight", () => { it("should find joined rooms", () => { cy.openSpotlightDialog() .within(() => { + cy.wait(500); // Wait for dialog to settle cy.spotlightSearch().clear().type(room1Name); cy.spotlightResults().should("have.length", 1); cy.spotlightResults().eq(0).should("contain", room1Name); @@ -254,6 +255,7 @@ describe("Spotlight", () => { it("should find known public rooms", () => { cy.openSpotlightDialog() .within(() => { + cy.wait(500); // Wait for dialog to settle cy.spotlightFilter(Filter.PublicRooms); cy.spotlightSearch().clear().type(room1Name); cy.spotlightResults().should("have.length", 1); @@ -270,6 +272,7 @@ describe("Spotlight", () => { it("should find unknown public rooms", () => { cy.openSpotlightDialog() .within(() => { + cy.wait(500); // Wait for dialog to settle cy.spotlightFilter(Filter.PublicRooms); cy.spotlightSearch().clear().type(room2Name); cy.spotlightResults().should("have.length", 1); @@ -287,6 +290,7 @@ describe("Spotlight", () => { it("should find unknown public world readable rooms", () => { cy.openSpotlightDialog() .within(() => { + cy.wait(500); // Wait for dialog to settle cy.spotlightFilter(Filter.PublicRooms); cy.spotlightSearch().clear().type(room3Name); cy.spotlightResults().should("have.length", 1); @@ -306,6 +310,7 @@ describe("Spotlight", () => { it.skip("should find unknown public rooms on other homeservers", () => { cy.openSpotlightDialog() .within(() => { + cy.wait(500); // Wait for dialog to settle cy.spotlightFilter(Filter.PublicRooms); cy.spotlightSearch().clear().type(room3Name); cy.get("[aria-haspopup=true][role=button]").click(); @@ -318,6 +323,7 @@ describe("Spotlight", () => { }) .then(() => cy.spotlightDialog().within(() => { + cy.wait(500); // Wait for dialog to settle cy.spotlightResults().should("have.length", 1); cy.spotlightResults().eq(0).should("contain", room3Name); cy.spotlightResults().eq(0).should("contain", room3Id); @@ -328,6 +334,7 @@ describe("Spotlight", () => { it("should find known people", () => { cy.openSpotlightDialog() .within(() => { + cy.wait(500); // Wait for dialog to settle cy.spotlightFilter(Filter.People); cy.spotlightSearch().clear().type(bot1Name); cy.spotlightResults().should("have.length", 1); @@ -342,6 +349,7 @@ describe("Spotlight", () => { it("should find unknown people", () => { cy.openSpotlightDialog() .within(() => { + cy.wait(500); // Wait for dialog to settle cy.spotlightFilter(Filter.People); cy.spotlightSearch().clear().type(bot2Name); cy.spotlightResults().should("have.length", 1); @@ -359,6 +367,7 @@ describe("Spotlight", () => { // Starting a DM with ByteBot (will be turned into a group dm later) cy.openSpotlightDialog().within(() => { + cy.wait(500); // Wait for dialog to settle cy.spotlightFilter(Filter.People); cy.spotlightSearch().clear().type(bot2Name); cy.spotlightResults().should("have.length", 1); @@ -414,6 +423,7 @@ describe("Spotlight", () => { // Test against https://github.com/vector-im/element-web/issues/22851 it("should show each person result only once", () => { cy.openSpotlightDialog().within(() => { + cy.wait(500); // Wait for dialog to settle cy.spotlightFilter(Filter.People); // 2 rounds of search to simulate the bug conditions. Specifically, the first search @@ -434,6 +444,7 @@ describe("Spotlight", () => { it("should allow opening group chat dialog", () => { cy.openSpotlightDialog() .within(() => { + cy.wait(500); // Wait for dialog to settle cy.spotlightFilter(Filter.People); cy.spotlightSearch().clear().type(bot2Name); cy.wait(3000); // wait for the dialog code to settle @@ -457,6 +468,7 @@ describe("Spotlight", () => { cy.visit("/#/home"); cy.openSpotlightDialog().within(() => { + cy.wait(500); // Wait for dialog to settle cy.spotlightFilter(Filter.People); cy.spotlightSearch().clear().type(bot1Name); cy.wait(3000); // wait for the dialog code to settle @@ -467,6 +479,7 @@ describe("Spotlight", () => { it("should be able to navigate results via keyboard", () => { cy.openSpotlightDialog().within(() => { + cy.wait(500); // Wait for dialog to settle cy.spotlightFilter(Filter.People); cy.spotlightSearch().clear().type("b"); // our debouncing logic only starts the search after a short timeout, @@ -475,6 +488,7 @@ describe("Spotlight", () => { cy.get(".mx_Spinner") .should("not.exist") .then(() => { + cy.wait(500); // Wait to settle again cy.spotlightResults() .should("have.length", 2) .then(() => { diff --git a/cypress/plugins/dendritedocker/index.ts b/cypress/plugins/dendritedocker/index.ts index 3bb11c224d..f89f898121 100644 --- a/cypress/plugins/dendritedocker/index.ts +++ b/cypress/plugins/dendritedocker/index.ts @@ -96,7 +96,7 @@ async function dendritePineconeStart(template: string): Promise { let dendriteImage = "matrixdotorg/dendrite-monolith:main"; - let dendriteEntrypoint = "/usr/bin/dendrite-monolith-server"; + let dendriteEntrypoint = "/usr/bin/dendrite"; if (usePinecone) { dendriteImage = "matrixdotorg/dendrite-demo-pinecone:main"; dendriteEntrypoint = "/usr/bin/dendrite-demo-pinecone"; diff --git a/docs/cypress.md b/docs/cypress.md index 3ef251e9a9..91ec314bac 100644 --- a/docs/cypress.md +++ b/docs/cypress.md @@ -24,11 +24,15 @@ need to have Docker installed and working in order to run the Cypress tests. There are a few different ways to run the tests yourself. The simplest is to run: ``` +docker pull matrixdotorg/synapse:develop yarn run test:cypress ``` This will run the Cypress tests once, non-interactively. +Note: you don't need to run the `docker pull` command every time, but you should +do it regularly to ensure you are running against an up-to-date Synapse. + You can also run individual tests this way too, as you'd expect: ``` @@ -45,7 +49,7 @@ yarn run test:cypress:open ### Matching the CI environment In our Continuous Integration environment, we run the Cypress tests in the -Chrome browser. +Chrome browser, and with the latest Synapse image from Docker Hub. In some rare cases, tests behave differently between different browsers, so if you see CI failures for the Cypress tests, but those tests work OK on your local @@ -64,6 +68,17 @@ Note that you will need to have Chrome installed on your system to run the tests inside those browsers, whereas the default is to use Electron, which is included within the Cypress dependency. +Another cause of inconsistency between local and CI is the Synapse version. The +first time you run the tests, they automatically fetch the latest Docker image +of Synapse, but this won't update again unless you do it explicitly. To update +the Synapse you are using, run: + +``` +docker pull matrixdotorg/synapse:develop +``` + +and then run the tests as normal. + ### Running with Rust cryptography `matrix-js-sdk` is currently in the diff --git a/package.json b/package.json index 99bc0c0db9..eef4677435 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "3.79.0", + "version": "3.80.1", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { @@ -69,7 +69,7 @@ "@sentry/tracing": "^7.0.0", "@testing-library/react-hooks": "^8.0.1", "@vector-im/compound-design-tokens": "^0.0.5", - "@vector-im/compound-web": "^0.2.3", + "@vector-im/compound-web": "^0.4.0", "await-lock": "^2.1.0", "blurhash": "^1.1.3", "classnames": "^2.2.6", @@ -80,7 +80,7 @@ "emojibase-regex": "15.0.0", "escape-html": "^1.0.3", "file-saver": "^2.0.5", - "filesize": "10.0.7", + "filesize": "10.0.12", "focus-visible": "^5.2.0", "gfm.css": "^1.1.2", "glob-to-regexp": "^0.4.1", @@ -106,7 +106,7 @@ "opus-recorder": "^8.0.3", "pako": "^2.0.3", "png-chunks-extract": "^1.0.0", - "posthog-js": "1.73.1", + "posthog-js": "1.77.2", "proposal-temporal": "^0.9.0", "qrcode": "1.5.3", "re-resizable": "^6.9.0", @@ -159,7 +159,7 @@ "@types/fs-extra": "^11.0.0", "@types/geojson": "^7946.0.8", "@types/glob-to-regexp": "^0.4.1", - "@types/jest": "29.5.3", + "@types/jest": "29.5.4", "@types/katex": "^0.16.0", "@types/lodash": "^4.14.168", "@types/modernizr": "^3.5.3", @@ -181,23 +181,23 @@ "@typescript-eslint/eslint-plugin": "^5.35.1", "@typescript-eslint/parser": "^5.6.0", "allchange": "^1.1.0", - "axe-core": "4.7.2", + "axe-core": "4.8.0", "babel-jest": "^29.0.0", "blob-polyfill": "^7.0.0", "chokidar": "^3.5.1", - "cypress": "^12.0.0", + "cypress": "^13.0.0", "cypress-axe": "^1.0.0", "cypress-multi-reporters": "^1.6.1", "cypress-real-events": "^1.7.1", "cypress-terminal-report": "^5.3.2", - "eslint": "8.45.0", + "eslint": "8.48.0", "eslint-config-google": "^0.14.0", "eslint-config-prettier": "^9.0.0", - "eslint-plugin-deprecate": "^0.7.0", + "eslint-plugin-deprecate": "0.7.0", "eslint-plugin-import": "^2.25.4", "eslint-plugin-jest": "^27.2.1", "eslint-plugin-jsx-a11y": "^6.5.1", - "eslint-plugin-matrix-org": "1.2.0", + "eslint-plugin-matrix-org": "1.2.1", "eslint-plugin-react": "^7.28.0", "eslint-plugin-react-hooks": "^4.3.0", "eslint-plugin-unicorn": "^48.0.0", diff --git a/res/css/compound/_SuccessDialog.pcss b/res/css/compound/_SuccessDialog.pcss index 61f98a97df..9085cedc11 100644 --- a/res/css/compound/_SuccessDialog.pcss +++ b/res/css/compound/_SuccessDialog.pcss @@ -18,7 +18,7 @@ limitations under the License. text-align: center; .mx_Icon { - mask-border: $spacing-16; + margin-bottom: $spacing-16; } .mx_Dialog_header { diff --git a/res/css/views/avatars/_BaseAvatar.pcss b/res/css/views/avatars/_BaseAvatar.pcss index dd643ebfb5..c8e2360aaf 100644 --- a/res/css/views/avatars/_BaseAvatar.pcss +++ b/res/css/views/avatars/_BaseAvatar.pcss @@ -24,8 +24,9 @@ limitations under the License. } button.mx_BaseAvatar { - /* The user agent stylesheet overrides the font-size in this scenario - And that breaks the alignment, emojis, and all sorts of things + /*