From eedeb2cdafc4c012b1bb42cc332fbc9f11221745 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 17 Oct 2024 08:31:51 +0000 Subject: [PATCH 01/14] Update dependency @types/node to v18.19.56 (#28137) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 8ac810593d2..15af723869b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3193,9 +3193,9 @@ undici-types "~6.19.2" "@types/node@18": - version "18.19.55" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.55.tgz#29c3f8e1485a92ec96636957ddec55aabc6e856e" - integrity sha512-zzw5Vw52205Zr/nmErSEkN5FLqXPuKX/k5d1D7RKHATGqU7y6YfX9QxZraUzUrFGqH6XzOzG196BC35ltJC4Cw== + version "18.19.56" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.56.tgz#e46d3e6e5417d3b89e89ee6207d3c2cba7ad1ef2" + integrity sha512-4EMJlWwwGnVPflJAtM14p9eVSa6BOv5b92mCsh5zcM1UagNtEtrbbtaE6WE1tw2TabavatnwqXjlIpcAEuJJNg== dependencies: undici-types "~5.26.4" From 2cff2b5a869fc73b342e456b5c1d3837dfd9882a Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 17 Oct 2024 11:10:32 +0100 Subject: [PATCH 02/14] Pin GHA runner versions (#28215) Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .github/workflows/backport.yml | 2 +- .github/workflows/build.yml | 10 +++++----- .github/workflows/build_debian.yaml | 2 +- .github/workflows/build_develop.yml | 2 +- .github/workflows/dockerhub.yaml | 2 +- .github/workflows/docs.yml | 4 ++-- .github/workflows/issue_closed.yml | 2 +- .github/workflows/pending-reviews.yaml | 2 +- .github/workflows/release.yml | 4 ++-- .github/workflows/release_prepare.yml | 2 +- .github/workflows/static_analysis.yaml | 10 +++++----- .github/workflows/triage-assigned.yml | 2 +- .github/workflows/triage-incoming.yml | 2 +- .github/workflows/triage-labelled.yml | 20 +++++++++---------- .../workflows/triage-move-review-requests.yml | 4 ++-- .../workflows/triage-stale-flaky-tests.yml | 2 +- .github/workflows/triage-unlabelled.yml | 4 ++-- .github/workflows/update-jitsi.yml | 2 +- .github/workflows/update-topics.yaml | 8 ++++---- 19 files changed, 43 insertions(+), 43 deletions(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index dd793898eca..7252c27b5fb 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -10,7 +10,7 @@ on: jobs: backport: name: Backport - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 # Only react to merged PRs for security reasons. # See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target. if: > diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 84bb3d263d2..55f5c1f4a38 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,15 +18,15 @@ jobs: fail-fast: false matrix: image: - - ubuntu-latest - - windows-latest - - macos-latest + - ubuntu-24.04 + - windows-2022 + - macos-14 isDevelop: - ${{ github.event_name == 'push' && github.ref_name == 'develop' }} - # Skip the ubuntu-latest build for the develop branch as the dedicated CD build_develop workflow handles that + # Skip the ubuntu-24.04 build for the develop branch as the dedicated CD build_develop workflow handles that exclude: - isDevelop: true - image: ubuntu-latest + image: ubuntu-24.04 runs-on: ${{ matrix.image }} defaults: run: diff --git a/.github/workflows/build_debian.yaml b/.github/workflows/build_debian.yaml index 254b99d261a..319dccd9f2f 100644 --- a/.github/workflows/build_debian.yaml +++ b/.github/workflows/build_debian.yaml @@ -7,7 +7,7 @@ jobs: build: name: Build package environment: packages.element.io - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 env: R2_INCOMING_BUCKET: ${{ vars.R2_INCOMING_BUCKET }} R2_URL: ${{ vars.CF_R2_S3_API }} diff --git a/.github/workflows/build_develop.yml b/.github/workflows/build_develop.yml index 2407d2e6f1a..b4c96c4eef4 100644 --- a/.github/workflows/build_develop.yml +++ b/.github/workflows/build_develop.yml @@ -14,7 +14,7 @@ jobs: name: "Build & Deploy develop.element.io" # Only respect triggers from our develop branch, ignore that of forks if: github.repository == 'element-hq/element-web' - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 environment: develop env: R2_BUCKET: "element-web-develop" diff --git a/.github/workflows/dockerhub.yaml b/.github/workflows/dockerhub.yaml index c8bec61feaf..2fdc66fb338 100644 --- a/.github/workflows/dockerhub.yaml +++ b/.github/workflows/dockerhub.yaml @@ -13,7 +13,7 @@ permissions: jobs: buildx: name: Docker Buildx - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 environment: dockerhub strategy: fail-fast: false diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index e0bf8a6b8ec..c3f08deb1d4 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -17,7 +17,7 @@ concurrency: jobs: build: name: GitHub Pages - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Fetch element-desktop uses: actions/checkout@v4 @@ -99,7 +99,7 @@ jobs: environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 needs: build steps: - name: Deploy to GitHub Pages diff --git a/.github/workflows/issue_closed.yml b/.github/workflows/issue_closed.yml index ddcd3d12bd9..191f345cc9b 100644 --- a/.github/workflows/issue_closed.yml +++ b/.github/workflows/issue_closed.yml @@ -7,7 +7,7 @@ on: jobs: tidy: name: Tidy closed issues - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/github-script@v7 id: main diff --git a/.github/workflows/pending-reviews.yaml b/.github/workflows/pending-reviews.yaml index f22483547d9..499da6a9b3b 100644 --- a/.github/workflows/pending-reviews.yaml +++ b/.github/workflows/pending-reviews.yaml @@ -9,7 +9,7 @@ concurrency: ${{ github.workflow }} jobs: bot: name: Pending reviews bot - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 environment: Matrix env: URL: "https://github.com/pulls?q=is%3Apr+is%3Aopen+repo%3Amatrix-org%2Fmatrix-js-sdk+repo%3Amatrix-org%2Fmatrix-react-sdk+repo%3Aelement-hq%2Felement-web+repo%3Aelement-hq%2Felement-desktop+review-requested%3A%40me+sort%3Aupdated-desc+" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 799c439f739..0e8c21e7869 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,7 +27,7 @@ jobs: notify-downstream: name: Trigger release drafter downstream needs: release - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Notify element-desktop repo that element-web release has completed to re-trigger release-drafter uses: benc-uk/workflow-dispatch@e2e5e9a103e331dad343f381a29e654aea3cf8fc # v1 @@ -41,7 +41,7 @@ jobs: check: name: Post release checks needs: release - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Wait for dockerhub uses: t3chguy/wait-on-check-action@18541021811b56544d90e0f073401c2b99e249d6 # fork diff --git a/.github/workflows/release_prepare.yml b/.github/workflows/release_prepare.yml index f88e10493d6..ce088a93276 100644 --- a/.github/workflows/release_prepare.yml +++ b/.github/workflows/release_prepare.yml @@ -19,7 +19,7 @@ on: default: true jobs: prepare: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Checkout Element Desktop uses: actions/checkout@v4 diff --git a/.github/workflows/static_analysis.yaml b/.github/workflows/static_analysis.yaml index 470e0728925..536f78e18dd 100644 --- a/.github/workflows/static_analysis.yaml +++ b/.github/workflows/static_analysis.yaml @@ -19,7 +19,7 @@ env: jobs: ts_lint: name: "Typescript Syntax Check" - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 @@ -81,7 +81,7 @@ jobs: js_lint: name: "ESLint" - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 @@ -99,7 +99,7 @@ jobs: style_lint: name: "Style Lint" - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 @@ -117,7 +117,7 @@ jobs: workflow_lint: name: "Workflow Lint" - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 @@ -135,7 +135,7 @@ jobs: analyse_dead_code: name: "Analyse Dead Code" - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/triage-assigned.yml b/.github/workflows/triage-assigned.yml index 98112d556f2..81d1dff80fd 100644 --- a/.github/workflows/triage-assigned.yml +++ b/.github/workflows/triage-assigned.yml @@ -6,7 +6,7 @@ on: jobs: web-app-team: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 if: | contains(github.event.issue.assignees.*.login, 't3chguy') || contains(github.event.issue.assignees.*.login, 'andybalaam') || diff --git a/.github/workflows/triage-incoming.yml b/.github/workflows/triage-incoming.yml index 47ed03efe35..e63017dc3be 100644 --- a/.github/workflows/triage-incoming.yml +++ b/.github/workflows/triage-incoming.yml @@ -6,7 +6,7 @@ on: jobs: automate-project-columns: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/add-to-project@main with: diff --git a/.github/workflows/triage-labelled.yml b/.github/workflows/triage-labelled.yml index 0ee40b987ff..0112f180c19 100644 --- a/.github/workflows/triage-labelled.yml +++ b/.github/workflows/triage-labelled.yml @@ -11,7 +11,7 @@ on: jobs: apply_Z-Labs_label: name: Add Z-Labs label for features behind labs flags - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 if: > contains(github.event.issue.labels.*.name, 'A-Maths') || contains(github.event.issue.labels.*.name, 'A-Location-Sharing') || @@ -37,7 +37,7 @@ jobs: apply_Help-Wanted_label: name: Add "Help Wanted" label to all "good first issue" and Hacktoberfest - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 if: > contains(github.event.issue.labels.*.name, 'good first issue') || contains(github.event.issue.labels.*.name, 'Hacktoberfest') @@ -54,7 +54,7 @@ jobs: move_needs_info_issues: name: X-Needs-Info issues to Need info column on triage board - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 if: > contains(github.event.issue.labels.*.name, 'X-Needs-Info') steps: @@ -77,7 +77,7 @@ jobs: move_flakey_test_issues: name: Z-Flaky-Test issues to Sized for maintainer column on triage board - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 if: > contains(github.event.issue.labels.*.name, 'Z-Flaky-Test') steps: @@ -100,7 +100,7 @@ jobs: add_priority_design_issues_to_project: name: P1 X-Needs-Design to Design project board - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 if: > contains(github.event.issue.labels.*.name, 'X-Needs-Design') && (contains(github.event.issue.labels.*.name, 'S-Critical') && @@ -117,7 +117,7 @@ jobs: add_product_issues: name: X-Needs-Product to product project board - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 if: > contains(github.event.issue.labels.*.name, 'X-Needs-Product') steps: @@ -128,7 +128,7 @@ jobs: Search_issues_to_board: name: Search issues to project board - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 if: > contains(github.event.issue.labels.*.name, 'A-New-Search-Experience') steps: @@ -139,7 +139,7 @@ jobs: voip: name: Add labelled issues to VoIP project board - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 if: > contains(github.event.issue.labels.*.name, 'Team: VoIP') steps: @@ -150,7 +150,7 @@ jobs: verticals_feature: name: Add labelled issues to Verticals Feature project - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 if: > contains(github.event.issue.labels.*.name, 'Team: Verticals Feature') steps: @@ -161,7 +161,7 @@ jobs: tech_debt: name: Add labelled issues to tech debt project - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 if: > contains(github.event.issue.labels.*.name, 'A-Developer-Experience') || contains(github.event.issue.labels.*.name, 'A-Documentation') || diff --git a/.github/workflows/triage-move-review-requests.yml b/.github/workflows/triage-move-review-requests.yml index 9d2924c1a92..72d9786a4a9 100644 --- a/.github/workflows/triage-move-review-requests.yml +++ b/.github/workflows/triage-move-review-requests.yml @@ -6,7 +6,7 @@ on: jobs: add_design_pr_to_project: name: Move PRs asking for design review to the design board - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: octokit/graphql-action@v2.x id: find_team_members @@ -73,7 +73,7 @@ jobs: add_product_pr_to_project: name: Move PRs asking for design review to the design board - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: octokit/graphql-action@v2.x id: find_team_members diff --git a/.github/workflows/triage-stale-flaky-tests.yml b/.github/workflows/triage-stale-flaky-tests.yml index ab6693675f6..d339a136cd4 100644 --- a/.github/workflows/triage-stale-flaky-tests.yml +++ b/.github/workflows/triage-stale-flaky-tests.yml @@ -4,7 +4,7 @@ on: - cron: "30 1 * * *" jobs: close: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 permissions: actions: write issues: write diff --git a/.github/workflows/triage-unlabelled.yml b/.github/workflows/triage-unlabelled.yml index 59d1bf471d1..1cd1c80afc3 100644 --- a/.github/workflows/triage-unlabelled.yml +++ b/.github/workflows/triage-unlabelled.yml @@ -7,7 +7,7 @@ on: jobs: Move_Unabeled_Issue_On_Project_Board: name: Move no longer X-Needs-Info issues to Triaged - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 if: > ${{ !contains(github.event.issue.labels.*.name, 'X-Needs-Info') }} @@ -44,7 +44,7 @@ jobs: remove_Z-Labs_label: name: Remove Z-Labs label when features behind labs flags are removed - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 if: > !(contains(github.event.issue.labels.*.name, 'A-Maths') || contains(github.event.issue.labels.*.name, 'A-Message-Pinning') || diff --git a/.github/workflows/update-jitsi.yml b/.github/workflows/update-jitsi.yml index 71405177d27..68dbf22e631 100644 --- a/.github/workflows/update-jitsi.yml +++ b/.github/workflows/update-jitsi.yml @@ -6,7 +6,7 @@ on: - cron: "0 3 * * 0" # 3am every Sunday jobs: update: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/update-topics.yaml b/.github/workflows/update-topics.yaml index b24c84ad264..a984fc4f038 100644 --- a/.github/workflows/update-topics.yaml +++ b/.github/workflows/update-topics.yaml @@ -18,7 +18,7 @@ concurrency: ${{ github.workflow }} jobs: bot: name: Release topic update - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 environment: Matrix steps: - uses: actions/github-script@v7 @@ -56,7 +56,7 @@ jobs: headers, body: "{}", }); - + let res = await fetch(apiUrl, { method: "GET", headers, @@ -69,7 +69,7 @@ jobs: const data = await res.json(); console.log(roomId, "got event", data); - + const topic = data.topic.replace(regex, releaseTopic); if (topic === data.topic) { console.log(roomId, "nothing to do"); @@ -89,7 +89,7 @@ jobs: }), headers, }); - + if (res.ok) { console.log(roomId, "topic updated:", topic); } else { From 0c19991e3c6036b9f8fcb46dd43a20aeacccfaa8 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 17 Oct 2024 13:40:04 +0100 Subject: [PATCH 03/14] Fix network dropdown missing checkbox & aria-checked (#28220) Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../structures/GenericDropdownMenu.tsx | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/components/structures/GenericDropdownMenu.tsx b/src/components/structures/GenericDropdownMenu.tsx index 3e3d415371c..b323d9b8ade 100644 --- a/src/components/structures/GenericDropdownMenu.tsx +++ b/src/components/structures/GenericDropdownMenu.tsx @@ -106,6 +106,10 @@ type IProps = WithKeyFunction & { AdditionalOptions?: FunctionComponent; }; +function calculateKey(value: T, toKey: ((key: T) => Key) | undefined): Key { + return toKey ? toKey(value) : (value as Key); +} + export function GenericDropdownMenu({ value, onChange, @@ -119,23 +123,24 @@ export function GenericDropdownMenu({ }: IProps): JSX.Element { const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu(); + const valueKey = calculateKey(value, toKey); const selected: GenericDropdownMenuItem | undefined = options .flatMap((it) => (isGenericDropdownMenuGroup(it) ? [it, ...it.options] : [it])) - .find((option) => (toKey ? toKey(option.key) === toKey(value) : option.key === value)); + .find((option) => calculateKey(option.key, toKey) === valueKey); let contextMenuOptions: JSX.Element; if (options && isGenericDropdownMenuGroupArray(options)) { contextMenuOptions = ( <> {options.map((group) => ( {group.options.map((option) => ( { @@ -144,7 +149,7 @@ export function GenericDropdownMenu({ onClose?.(ev); }} adornment={option.adornment} - isSelected={option === selected} + isSelected={calculateKey(option.key, toKey) === valueKey} /> ))} @@ -156,7 +161,7 @@ export function GenericDropdownMenu({ <> {options.map((option) => ( { @@ -165,7 +170,7 @@ export function GenericDropdownMenu({ onClose?.(ev); }} adornment={option.adornment} - isSelected={option === selected} + isSelected={calculateKey(option.key, toKey) === valueKey} /> ))} From 1fc01225239c6a88047b0f9b67171c3dfba09848 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 17 Oct 2024 15:44:53 +0100 Subject: [PATCH 04/14] Deflake unskippable verification (#28222) Discovering what is the correct way of asserting that an element is *not* on screen with Playwright, which apparently is nontrivial. --- playwright/e2e/login/login.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playwright/e2e/login/login.spec.ts b/playwright/e2e/login/login.spec.ts index 696ecb183c8..e1307f7402d 100644 --- a/playwright/e2e/login/login.spec.ts +++ b/playwright/e2e/login/login.spec.ts @@ -217,7 +217,7 @@ test.describe("Login", () => { const h1 = await page.getByRole("heading", { name: "Verify this device", level: 1 }); await expect(h1).toBeVisible(); - expect(h1.locator(".mx_CompleteSecurity_skip")).not.toBeVisible(); + await expect(h1.locator(".mx_CompleteSecurity_skip")).toHaveCount(0); }); }); }); From a9bea774f988ea663226815c3b46ccfa6ff8a44b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 17 Oct 2024 16:02:38 +0100 Subject: [PATCH 05/14] Fix Ctrl+F shortcut not working with minimised room summary card (#28223) Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/stores/right-panel/RightPanelStore.ts | 1 + src/stores/right-panel/RightPanelStoreIPanelState.ts | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/stores/right-panel/RightPanelStore.ts b/src/stores/right-panel/RightPanelStore.ts index 94637b9cd45..9da06580dc5 100644 --- a/src/stores/right-panel/RightPanelStore.ts +++ b/src/stores/right-panel/RightPanelStore.ts @@ -80,6 +80,7 @@ export default class RightPanelStore extends ReadyWatchingStore { if (this.currentCard.phase !== RightPanelPhases.RoomSummary) { this.setCard({ phase: RightPanelPhases.RoomSummary, state: { focusRoomSearch: true } }); } + this.show(null); } } } diff --git a/src/stores/right-panel/RightPanelStoreIPanelState.ts b/src/stores/right-panel/RightPanelStoreIPanelState.ts index deffe045e64..afb74425636 100644 --- a/src/stores/right-panel/RightPanelStoreIPanelState.ts +++ b/src/stores/right-panel/RightPanelStoreIPanelState.ts @@ -40,6 +40,8 @@ export interface IRightPanelCardStateStored { initialEventId?: string; isInitialEventHighlighted?: boolean; initialEventScrollIntoView?: boolean; + // room summary card + focusRoomSearch?: boolean; } export interface IRightPanelCard { @@ -85,6 +87,7 @@ export function convertCardToStore(panelState: IRightPanelCard): IRightPanelCard memberInfoEventId: !!state?.memberInfoEvent?.getId() ? state.memberInfoEvent.getId() : undefined, initialEventId: !!state?.initialEvent?.getId() ? state.initialEvent.getId() : undefined, memberId: !!state?.member?.userId ? state.member.userId : undefined, + focusRoomSearch: state.focusRoomSearch, }; return { state: stateStored, phase: panelState.phase }; @@ -105,6 +108,7 @@ function convertStoreToCard(panelStateStore: IRightPanelCardStored, room: Room): : undefined, initialEvent: !!stateStored?.initialEventId ? room.findEventById(stateStored.initialEventId) : undefined, member: (!!stateStored?.memberId && room.getMember(stateStored.memberId)) || undefined, + focusRoomSearch: stateStored?.focusRoomSearch, }; return { state: state, phase: panelStateStore.phase }; From cb383efb4283680eb05691dc372fab339e0f2005 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 17 Oct 2024 16:45:56 +0100 Subject: [PATCH 06/14] Port the flaky add/remove 3pid test to playwright (#28226) Fixes https://github.com/element-hq/element-web/issues/28160 --- .../account-user-settings-tab.spec.ts | 47 +++++++++++++++++ .../settings/AddRemoveThreepids-test.tsx | 51 ------------------- 2 files changed, 47 insertions(+), 51 deletions(-) diff --git a/playwright/e2e/settings/account-user-settings-tab.spec.ts b/playwright/e2e/settings/account-user-settings-tab.spec.ts index ce54995d453..5492094f937 100644 --- a/playwright/e2e/settings/account-user-settings-tab.spec.ts +++ b/playwright/e2e/settings/account-user-settings-tab.spec.ts @@ -140,4 +140,51 @@ test.describe("Account user settings tab", () => { await expect(page.locator(".mx_UserMenu .mx_BaseAvatar").getByText("A")).toBeVisible(); // Alice await expect(page.locator(".mx_RoomView_wrapper .mx_BaseAvatar").getByText("A")).toBeVisible(); // Alice }); + + // ported to a playwright test because the jest test was very flakey for no obvious reason + test("should display an error if the code is incorrect when adding a phone number", async ({ uut, page }) => { + const dummyUrl = "https://nowhere.dummy/_matrix/client/unstable/add_threepid/msisdn/submit_token"; + + await page.route( + `**/_matrix/client/v3/account/3pid/msisdn/requestToken`, + async (route) => { + await route.fulfill({ + json: { + success: true, + sid: "1", + msisdn: "447700900000", + intl_fmt: "+44 7700 900000", + submit_url: dummyUrl, + }, + }); + }, + { times: 1 }, + ); + + await page.route( + dummyUrl, + async (route) => { + await route.fulfill({ + status: 400, + json: { + errcode: "M_THREEPID_AUTH_FAILED", + error: "That code is definitely wrong", + }, + }); + }, + { times: 1 }, + ); + + const phoneSection = page.getByTestId("mx_AccountPhoneNumbers"); + await phoneSection.getByRole("textbox", { name: "Phone Number" }).fill("07700900000"); + await phoneSection.getByRole("button", { name: "Add" }).click(); + + await phoneSection + .getByRole("textbox", { name: "Verification code" }) + .fill("A small eurasian field mouse dancing the paso doble"); + + await phoneSection.getByRole("button", { name: "Continue" }).click(); + + await expect(page.getByRole("heading", { name: "Unable to verify phone number." })).toBeVisible(); + }); }); diff --git a/test/unit-tests/components/views/settings/AddRemoveThreepids-test.tsx b/test/unit-tests/components/views/settings/AddRemoveThreepids-test.tsx index 31b57596f8b..61a612fe8fa 100644 --- a/test/unit-tests/components/views/settings/AddRemoveThreepids-test.tsx +++ b/test/unit-tests/components/views/settings/AddRemoveThreepids-test.tsx @@ -254,57 +254,6 @@ describe("AddRemoveThreepids", () => { expect(onChangeFn).toHaveBeenCalled(); }, 10000); - it("should display an error if the code is incorrect", async () => { - const onChangeFn = jest.fn(); - const createDialogFn = jest.spyOn(Modal, "createDialog"); - mocked(client.requestAdd3pidMsisdnToken).mockResolvedValue({ - sid: "1", - msisdn: PHONE1.address, - intl_fmt: "+" + PHONE1.address, - success: true, - submit_url: "https://example.dummy", - }); - - render( - , - { - wrapper: clientProviderWrapper, - }, - ); - - const input = screen.getByRole("textbox", { name: "Phone Number" }); - await userEvent.type(input, PHONE1_LOCALNUM); - - const countryDropdown = screen.getByRole("button", { name: /Country Dropdown/ }); - await userEvent.click(countryDropdown); - const gbOption = screen.getByRole("option", { name: "🇬🇧 United Kingdom (+44)" }); - await userEvent.click(gbOption); - - const addButton = screen.getByRole("button", { name: /Add/ }); - await userEvent.click(addButton); - - mocked(client).addThreePidOnly.mockRejectedValueOnce(new Error("Unauthorized")); - - const verificationInput = screen.getByRole("textbox", { name: "Verification code" }); - await userEvent.type(verificationInput, "123457"); - - const continueButton = screen.getByRole("button", { name: /Continue/ }); - await userEvent.click(continueButton); - - expect(createDialogFn).toHaveBeenCalledWith(expect.anything(), { - description: "Unauthorized", - title: "Unable to verify phone number.", - }); - - expect(onChangeFn).not.toHaveBeenCalled(); - }); - it("should remove an email address", async () => { const onChangeFn = jest.fn(); render( From 8f353537589ca5e738dd4225b5f7ca9337f091e8 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 17 Oct 2024 17:08:26 +0100 Subject: [PATCH 07/14] Deflake keyboard navigation test (#28224) Fixes https://github.com/element-hq/element-web/issues/28139 --- playwright/e2e/accessibility/keyboard-navigation.spec.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/playwright/e2e/accessibility/keyboard-navigation.spec.ts b/playwright/e2e/accessibility/keyboard-navigation.spec.ts index 18983bde014..87cee4e05c3 100644 --- a/playwright/e2e/accessibility/keyboard-navigation.spec.ts +++ b/playwright/e2e/accessibility/keyboard-navigation.spec.ts @@ -15,6 +15,9 @@ test.describe("Landmark navigation tests", () => { }); test("without any rooms", async ({ page, homeserver, app, user }) => { + // sometimes the space button doesn't appear right away + await expect(page.locator(".mx_SpaceButton_active")).toBeVisible(); + /** * Without any rooms, there is no tile in the roomlist to be focused. * So the next landmark in the list should be focused instead. From e79916454f49ebeb12b41979c1c4dce0df064a79 Mon Sep 17 00:00:00 2001 From: ElementRobot Date: Fri, 18 Oct 2024 07:20:52 +0100 Subject: [PATCH 08/14] [create-pull-request] automated change (#28230) Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com> --- playwright/plugins/homeserver/synapse/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playwright/plugins/homeserver/synapse/index.ts b/playwright/plugins/homeserver/synapse/index.ts index fd1aa440689..cb4882f1b90 100644 --- a/playwright/plugins/homeserver/synapse/index.ts +++ b/playwright/plugins/homeserver/synapse/index.ts @@ -20,7 +20,7 @@ import { randB64Bytes } from "../../utils/rand"; // Docker tag to use for synapse docker image. // We target a specific digest as every now and then a Synapse update will break our CI. // This digest is updated by the playwright-image-updates.yaml workflow periodically. -const DOCKER_TAG = "develop@sha256:fd6ba2d8471a0807e1bccef4124b22d17f0058f2cf9285066fdd94d8c631964a"; +const DOCKER_TAG = "develop@sha256:47c62aa9507a24820190eef547861c0d278cc83fe90329c46b9f4329eed88ef4"; async function cfgDirFromTemplate(opts: StartHomeserverOpts): Promise> { const templateDir = path.join(__dirname, "templates", opts.template); From 85d2bf3a04faa5826b56136f0f44dce3cebb48c4 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Fri, 18 Oct 2024 09:51:53 +0200 Subject: [PATCH 09/14] Second batch: remove deprecated calls on `MatrixClient` (#28227) * Replace `MatrixClient.forceDiscardSession` call by `MatrixClient.CryptoApi.forceDiscardSession` * Remove `MatrixClient.scheduleAllGroupSessionsForBackup` mock --- src/SlashCommands.tsx | 2 +- test/test-utils/test-utils.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index 7375cba568e..02c2b338493 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -727,7 +727,7 @@ export const Commands = [ isEnabled: (cli) => !isCurrentLocalRoom(cli), runFn: function (cli, roomId) { try { - cli.forceDiscardSession(roomId); + cli.getCrypto()?.forceDiscardSession(roomId); } catch (e) { return reject(e instanceof Error ? e.message : e); } diff --git a/test/test-utils/test-utils.ts b/test/test-utils/test-utils.ts index d9f4f3f5a65..dd1f47ad6d4 100644 --- a/test/test-utils/test-utils.ts +++ b/test/test-utils/test-utils.ts @@ -166,7 +166,6 @@ export function createTestClient(): MatrixClient { }); }), mxcUrlToHttp: jest.fn().mockImplementation((mxc: string) => `http://this.is.a.url/${mxc.substring(6)}`), - scheduleAllGroupSessionsForBackup: jest.fn().mockResolvedValue(undefined), setAccountData: jest.fn(), setRoomAccountData: jest.fn(), setRoomTopic: jest.fn(), From 1bb482f6f73fc441795b70e9ae2028a7e98ff4a0 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Fri, 18 Oct 2024 11:45:45 +0200 Subject: [PATCH 10/14] Replace `Matrix.getKeyBackupEnabled` by `MatrixClient.CryptoApi.getActiveSessionBackupVersion` (#28225) * Migrating deprecated sync `MatrixClient.getKeyBackupEnabled` to async `MatrixClient.CryptoApi.getActiveSessionBackupVersion` in `NewRecoveryMethodDialog`. Rewrite `NewRecoveryMethodDialog` into a functional component to make it easier to handle the new async method. * Migrating deprecated sync `MatrixClient.getKeyBackupEnabled` to async `MatrixClient.CryptoApi.getActiveSessionBackupVersion` in `MatrixChat`. --- .../security/NewRecoveryMethodDialog.tsx | 128 +++++++-------- src/components/structures/MatrixChat.tsx | 7 +- test/test-utils/test-utils.ts | 1 + .../security/NewRecoveryMethodDialog-test.tsx | 81 ++++++++++ .../NewRecoveryMethodDialog-test.tsx.snap | 146 ++++++++++++++++++ .../components/structures/MatrixChat-test.tsx | 21 ++- 6 files changed, 312 insertions(+), 72 deletions(-) create mode 100644 test/unit-tests/async-components/dialogs/security/NewRecoveryMethodDialog-test.tsx create mode 100644 test/unit-tests/async-components/dialogs/security/__snapshots__/NewRecoveryMethodDialog-test.tsx.snap diff --git a/src/async-components/views/dialogs/security/NewRecoveryMethodDialog.tsx b/src/async-components/views/dialogs/security/NewRecoveryMethodDialog.tsx index ba08bcad23a..ac180397499 100644 --- a/src/async-components/views/dialogs/security/NewRecoveryMethodDialog.tsx +++ b/src/async-components/views/dialogs/security/NewRecoveryMethodDialog.tsx @@ -7,10 +7,8 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only Please see LICENSE files in the repository root for full details. */ -import React from "react"; -import { KeyBackupInfo } from "matrix-js-sdk/src/crypto-api"; +import React, { JSX, useEffect, useState } from "react"; -import { MatrixClientPeg } from "../../../../MatrixClientPeg"; import dis from "../../../../dispatcher/dispatcher"; import { _t } from "../../../../languageHandler"; import Modal from "../../../../Modal"; @@ -18,81 +16,73 @@ import RestoreKeyBackupDialog from "../../../../components/views/dialogs/securit import { Action } from "../../../../dispatcher/actions"; import DialogButtons from "../../../../components/views/elements/DialogButtons"; import BaseDialog from "../../../../components/views/dialogs/BaseDialog"; +import { useMatrixClientContext } from "../../../../contexts/MatrixClientContext.tsx"; -interface IProps { - newVersionInfo: KeyBackupInfo; +/** + * Properties for {@link NewRecoveryMethodDialog}. + */ +interface NewRecoveryMethodDialogProps { + /** + * Callback when the dialog is dismissed. + */ onFinished(): void; } -export default class NewRecoveryMethodDialog extends React.PureComponent { - private onOkClick = (): void => { - this.props.onFinished(); - }; +// Export as default instead of a named export so that it can be dynamically imported with `Modal.createDialogAsync` - private onGoToSettingsClick = (): void => { - this.props.onFinished(); - dis.fire(Action.ViewUserSettings); - }; +/** + * Dialog to inform the user that a new recovery method has been detected. + */ +export default function NewRecoveryMethodDialog({ onFinished }: NewRecoveryMethodDialogProps): JSX.Element { + const matrixClient = useMatrixClientContext(); + const [isKeyBackupEnabled, setIsKeyBackupEnabled] = useState(false); + useEffect(() => { + const checkBackupEnabled = async (): Promise => { + const crypto = matrixClient.getCrypto(); + setIsKeyBackupEnabled(Boolean(crypto && (await crypto.getActiveSessionBackupVersion()) !== null)); + }; - private onSetupClick = async (): Promise => { - Modal.createDialog( - RestoreKeyBackupDialog, - { - onFinished: this.props.onFinished, - }, - undefined, - /* priority = */ false, - /* static = */ true, - ); - }; + checkBackupEnabled(); + }, [matrixClient]); - public render(): React.ReactNode { - const title = ( - - {_t("encryption|new_recovery_method_detected|title")} - - ); - - const newMethodDetected =

{_t("encryption|new_recovery_method_detected|description_1")}

; - - const hackWarning = ( - {_t("encryption|new_recovery_method_detected|warning")} - ); - - let content: JSX.Element | undefined; - if (MatrixClientPeg.safeGet().getKeyBackupEnabled()) { - content = ( -
- {newMethodDetected} -

{_t("encryption|new_recovery_method_detected|description_2")}

- {hackWarning} - -
- ); + function onClick(): void { + if (isKeyBackupEnabled) { + onFinished(); } else { - content = ( -
- {newMethodDetected} - {hackWarning} - -
+ Modal.createDialog( + RestoreKeyBackupDialog, + { + onFinished, + }, + undefined, + false, + true, ); } - - return ( - - {content} - - ); } + + return ( + + {_t("encryption|new_recovery_method_detected|title")} + + } + > +

{_t("encryption|new_recovery_method_detected|description_1")}

+ {isKeyBackupEnabled &&

{_t("encryption|new_recovery_method_detected|description_2")}

} + {_t("encryption|new_recovery_method_detected|warning")} + { + onFinished(); + dis.fire(Action.ViewUserSettings); + }} + /> +
+ ); } diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 8b23455967b..0f122b2d5f9 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -1631,8 +1631,12 @@ export default class MatrixChat extends React.PureComponent { cli.on(CryptoEvent.KeyBackupFailed, async (errcode): Promise => { let haveNewVersion: boolean | undefined; let newVersionInfo: KeyBackupInfo | null = null; + const keyBackupEnabled = Boolean( + cli.getCrypto() && (await cli.getCrypto()?.getActiveSessionBackupVersion()) !== null, + ); + // if key backup is still enabled, there must be a new backup in place - if (cli.getKeyBackupEnabled()) { + if (keyBackupEnabled) { haveNewVersion = true; } else { // otherwise check the server to see if there's a new one @@ -1650,7 +1654,6 @@ export default class MatrixChat extends React.PureComponent { import( "../../async-components/views/dialogs/security/NewRecoveryMethodDialog" ) as unknown as Promise, - { newVersionInfo: newVersionInfo! }, ); } else { Modal.createDialogAsync( diff --git a/test/test-utils/test-utils.ts b/test/test-utils/test-utils.ts index dd1f47ad6d4..43807eb0301 100644 --- a/test/test-utils/test-utils.ts +++ b/test/test-utils/test-utils.ts @@ -129,6 +129,7 @@ export function createTestClient(): MatrixClient { getVerificationRequestsToDeviceInProgress: jest.fn().mockReturnValue([]), setDeviceIsolationMode: jest.fn(), prepareToEncrypt: jest.fn(), + getActiveSessionBackupVersion: jest.fn().mockResolvedValue(null), }), getPushActionsForEvent: jest.fn(), diff --git a/test/unit-tests/async-components/dialogs/security/NewRecoveryMethodDialog-test.tsx b/test/unit-tests/async-components/dialogs/security/NewRecoveryMethodDialog-test.tsx new file mode 100644 index 00000000000..fc964e57bf4 --- /dev/null +++ b/test/unit-tests/async-components/dialogs/security/NewRecoveryMethodDialog-test.tsx @@ -0,0 +1,81 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only + * Please see LICENSE files in the repository root for full details. + */ + +import React from "react"; +import { MatrixClient } from "matrix-js-sdk/src/matrix"; +import { render, screen } from "jest-matrix-react"; +import { waitFor } from "@testing-library/dom"; +import userEvent from "@testing-library/user-event"; +import { act } from "@testing-library/react-hooks/dom"; + +import NewRecoveryMethodDialog from "../../../../../src/async-components/views/dialogs/security/NewRecoveryMethodDialog"; +import { createTestClient } from "../../../../test-utils"; +import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext.tsx"; +import dis from "../../../../../src/dispatcher/dispatcher.ts"; +import { Action } from "../../../../../src/dispatcher/actions.ts"; +import Modal from "../../../../../src/Modal.tsx"; + +describe("", () => { + let matrixClient: MatrixClient; + beforeEach(() => { + matrixClient = createTestClient(); + jest.spyOn(dis, "fire"); + jest.spyOn(Modal, "createDialog"); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + function renderComponent(onFinished: () => void = jest.fn()) { + return render( + + + , + ); + } + + test("when cancel is clicked", async () => { + const onFinished = jest.fn(); + act(() => { + renderComponent(onFinished); + }); + + await userEvent.click(screen.getByRole("button", { name: "Go to Settings" })); + expect(onFinished).toHaveBeenCalled(); + expect(dis.fire).toHaveBeenCalledWith(Action.ViewUserSettings); + }); + + test("when key backup is enabled", async () => { + jest.spyOn(matrixClient.getCrypto()!, "getActiveSessionBackupVersion").mockResolvedValue("version"); + + const onFinished = jest.fn(); + + await act(async () => { + const { asFragment } = renderComponent(onFinished); + await waitFor(() => + expect( + screen.getByText("This session is encrypting history using the new recovery method."), + ).toBeInTheDocument(), + ); + expect(asFragment()).toMatchSnapshot(); + }); + + await userEvent.click(screen.getByRole("button", { name: "Set up Secure Messages" })); + expect(onFinished).toHaveBeenCalled(); + }); + + test("when key backup is disabled", async () => { + const onFinished = jest.fn(); + + const { asFragment } = renderComponent(onFinished); + expect(asFragment()).toMatchSnapshot(); + + await userEvent.click(screen.getByRole("button", { name: "Set up Secure Messages" })); + await waitFor(() => expect(Modal.createDialog).toHaveBeenCalled()); + }); +}); diff --git a/test/unit-tests/async-components/dialogs/security/__snapshots__/NewRecoveryMethodDialog-test.tsx.snap b/test/unit-tests/async-components/dialogs/security/__snapshots__/NewRecoveryMethodDialog-test.tsx.snap new file mode 100644 index 00000000000..08b5ee18d33 --- /dev/null +++ b/test/unit-tests/async-components/dialogs/security/__snapshots__/NewRecoveryMethodDialog-test.tsx.snap @@ -0,0 +1,146 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` when key backup is disabled 1`] = ` + +
+