Skip to content

Commit d51ba4b

Browse files
authored
Merge branch 'element-hq:develop' into kaylendog/msc3414
2 parents 8a08134 + 6bedb15 commit d51ba4b

File tree

11 files changed

+747
-125
lines changed

11 files changed

+747
-125
lines changed

.github/workflows/build.yml

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ concurrency:
1010
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
1111
# develop pushes and repository_dispatch handled in build_develop.yaml
1212
env:
13-
# These must be set for fetchdep.sh to get the right branch
14-
REPOSITORY: ${{ github.repository }}
13+
# This must be set for fetchdep.sh to get the right branch
1514
PR_NUMBER: ${{ github.event.pull_request.number }}
1615
permissions: {} # No permissions required
1716
jobs:
@@ -56,25 +55,15 @@ jobs:
5655
- run: yarn config set network-timeout 300000
5756

5857
- name: Fetch layered build
59-
id: layered_build
60-
env:
61-
# tell layered.sh to check out the right sha of the JS-SDK & EW, if they were given one
62-
JS_SDK_GITHUB_BASE_REF: ${{ inputs.matrix-js-sdk-sha }}
63-
run: |
64-
scripts/layered.sh
65-
JSSDK_SHA=$(git -C matrix-js-sdk rev-parse --short=12 HEAD)
66-
VECTOR_SHA=$(git rev-parse --short=12 HEAD)
67-
echo "VERSION=$VECTOR_SHA--js-$JSSDK_SHA" >> $GITHUB_OUTPUT
58+
run: ./scripts/layered.sh
6859

6960
- name: Copy config
7061
run: cp element.io/develop/config.json config.json
7162

7263
- name: Build
7364
env:
7465
CI_PACKAGE: true
75-
VERSION: "${{ steps.layered_build.outputs.VERSION }}"
76-
run: |
77-
yarn build
66+
run: VERSION=$(scripts/get-version-from-git.sh) yarn build
7867

7968
- name: Upload Artifact
8069
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4

.github/workflows/end-to-end-tests.yaml

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,25 +60,18 @@ jobs:
6060
node-version: "lts/*"
6161

6262
- name: Fetch layered build
63-
id: layered_build
6463
env:
6564
# tell layered.sh to check out the right sha of the JS-SDK & EW, if they were given one
6665
JS_SDK_GITHUB_BASE_REF: ${{ inputs.matrix-js-sdk-sha }}
67-
run: |
68-
scripts/layered.sh
69-
JSSDK_SHA=$(git -C matrix-js-sdk rev-parse --short=12 HEAD)
70-
VECTOR_SHA=$(git rev-parse --short=12 HEAD)
71-
echo "VERSION=$VECTOR_SHA--js-$JSSDK_SHA" >> $GITHUB_OUTPUT
66+
run: scripts/layered.sh
7267

7368
- name: Copy config
7469
run: cp element.io/develop/config.json config.json
7570

7671
- name: Build
7772
env:
7873
CI_PACKAGE: true
79-
VERSION: "${{ steps.layered_build.outputs.VERSION }}"
80-
run: |
81-
yarn build
74+
run: VERSION=$(scripts/get-version-from-git.sh) yarn build
8275

8376
- name: Upload Artifact
8477
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4

.github/workflows/static_analysis.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@ concurrency:
1212
cancel-in-progress: true
1313

1414
env:
15-
# These must be set for fetchdep.sh to get the right branch
16-
REPOSITORY: ${{ github.repository }}
15+
# This must be set for fetchdep.sh to get the right branch
1716
PR_NUMBER: ${{ github.event.pull_request.number }}
1817

1918
permissions: {} # No permissions required
Loading

playwright/testcontainers/synapse.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
77

88
import { SynapseContainer as BaseSynapseContainer } from "@element-hq/element-web-playwright-common/lib/testcontainers";
99

10-
const TAG = "develop@sha256:eb7ffb7077755c1493036196222063a0dc8179dae87f8fb6ec3b31a156f2b714";
10+
const TAG = "develop@sha256:5159141547ec7fc65e956226bd0f1349c5ef1e26ad72bfa25105f821cac74be2";
1111

1212
/**
1313
* SynapseContainer which freezes the docker digest to stabilise tests,

scripts/get-version-from-git.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
#!/usr/bin/env bash
22

3-
# Echoes a version based on the git hashes of the element-web, react-sdk & js-sdk checkouts, for the case where
3+
# Echoes a version based on the git hashes of the element-web & js-sdk checkouts, for the case where
44
# these dependencies are git checkouts.
55

66
set -e
77

8-
# Since the deps are fetched from git, we can rev-parse
8+
# Since the deps are fetched from git & linked, we can rev-parse
99
JSSDK_SHA=$(git -C node_modules/matrix-js-sdk rev-parse --short=12 HEAD)
1010
VECTOR_SHA=$(git rev-parse --short=12 HEAD) # use the ACTUAL SHA rather than assume develop
11-
echo $VECTOR_SHA-js-$JSSDK_SHA
11+
echo "$VECTOR_SHA-js-$JSSDK_SHA"

src/components/views/dialogs/VerificationRequestDialog.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export default class VerificationRequestDialog extends React.Component<IProps, I
6060
>
6161
<EncryptionPanel
6262
layout="dialog"
63-
verificationRequest={this.props.verificationRequest}
63+
verificationRequest={this.state.verificationRequest}
6464
verificationRequestPromise={this.props.verificationRequestPromise}
6565
onClose={this.props.onFinished}
6666
member={member}

src/components/views/settings/tabs/user/PreferencesUserSettingsTab.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ export default class PreferencesUserSettingsTab extends React.Component<IProps,
265265
</SettingsSubsection>
266266

267267
<SettingsSubsection heading={_t("settings|preferences|room_list_heading")}>
268-
{this.renderGroup(PreferencesUserSettingsTab.ROOM_LIST_SETTINGS)}
268+
{!newRoomListEnabled && this.renderGroup(PreferencesUserSettingsTab.ROOM_LIST_SETTINGS)}
269269
{/* The settings is on device level where the other room list settings are on account level */}
270270
{newRoomListEnabled && (
271271
<SettingsFlag name="RoomList.showMessagePreview" level={SettingLevel.DEVICE} />
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
/*
2+
Copyright 2025 New Vector Ltd.
3+
4+
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
5+
Please see LICENSE files in the repository root for full details.
6+
*/
7+
8+
import React from "react";
9+
import { act, render, screen } from "jest-matrix-react";
10+
import { User } from "matrix-js-sdk/src/matrix";
11+
import {
12+
type ShowSasCallbacks,
13+
VerificationPhase,
14+
type Verifier,
15+
type VerificationRequest,
16+
type ShowQrCodeCallbacks,
17+
} from "matrix-js-sdk/src/crypto-api";
18+
import { VerificationMethod } from "matrix-js-sdk/src/types";
19+
20+
import VerificationRequestDialog from "../../../../../src/components/views/dialogs/VerificationRequestDialog";
21+
import { stubClient } from "../../../../test-utils";
22+
23+
describe("VerificationRequestDialog", () => {
24+
function renderComponent(phase: VerificationPhase, method?: "emoji" | "qr"): ReturnType<typeof render> {
25+
const member = User.createUser("@alice:example.org", stubClient());
26+
const request = createRequest(phase, method);
27+
28+
return render(
29+
<VerificationRequestDialog onFinished={jest.fn()} member={member} verificationRequest={request} />,
30+
);
31+
}
32+
33+
it("Initially, asks how you would like to verify this device", async () => {
34+
const dialog = renderComponent(VerificationPhase.Ready);
35+
36+
expect(screen.getByRole("heading", { name: "Verify other device" })).toBeInTheDocument();
37+
expect(screen.getByText("Verify this device by completing one of the following:")).toBeInTheDocument();
38+
39+
expect(dialog.asFragment()).toMatchSnapshot();
40+
});
41+
42+
it("After we started verification here, says we are waiting for the other device", async () => {
43+
const dialog = renderComponent(VerificationPhase.Requested);
44+
45+
expect(screen.getByRole("heading", { name: "Verify other device" })).toBeInTheDocument();
46+
47+
expect(
48+
screen.getByText("To proceed, please accept the verification request on your other device."),
49+
).toBeInTheDocument();
50+
51+
expect(dialog.asFragment()).toMatchSnapshot();
52+
});
53+
54+
it("When other device accepted emoji, displays emojis and asks for confirmation", async () => {
55+
const dialog = renderComponent(VerificationPhase.Started, "emoji");
56+
57+
expect(screen.getByRole("heading", { name: "Verify other device" })).toBeInTheDocument();
58+
59+
expect(
60+
screen.getByText("Confirm the emoji below are displayed on both devices, in the same order:"),
61+
).toBeInTheDocument();
62+
63+
expect(dialog.asFragment()).toMatchSnapshot();
64+
});
65+
66+
it("After scanning QR, shows confirmation dialog", async () => {
67+
const dialog = renderComponent(VerificationPhase.Started, "qr");
68+
69+
expect(screen.getByRole("heading", { name: "Verify other device" })).toBeInTheDocument();
70+
expect(screen.getByRole("heading", { name: "Verify by scanning" })).toBeInTheDocument();
71+
72+
expect(screen.getByText("Almost there! Is your other device showing the same shield?")).toBeInTheDocument();
73+
74+
expect(dialog.asFragment()).toMatchSnapshot();
75+
});
76+
77+
it("Shows a successful message if verification finished normally", async () => {
78+
const dialog = renderComponent(VerificationPhase.Done);
79+
80+
expect(screen.getByRole("heading", { name: "Verify other device" })).toBeInTheDocument();
81+
expect(screen.getByText("You've successfully verified your device!")).toBeInTheDocument();
82+
83+
expect(dialog.asFragment()).toMatchSnapshot();
84+
});
85+
86+
it("Shows a failure message if verification was cancelled", async () => {
87+
const dialog = renderComponent(VerificationPhase.Cancelled);
88+
89+
expect(screen.getByRole("heading", { name: "Verify other device" })).toBeInTheDocument();
90+
expect(screen.getByRole("heading", { name: "Verification cancelled" })).toBeInTheDocument();
91+
92+
expect(
93+
screen.getByText(
94+
"You cancelled verification on your other device. Start verification again from the notification.",
95+
),
96+
).toBeInTheDocument();
97+
98+
expect(dialog.asFragment()).toMatchSnapshot();
99+
});
100+
101+
it("Renders correctly if the request is supplied later via a promise", async () => {
102+
// Given we supply a promise of a request instead of a request
103+
const member = User.createUser("@alice:example.org", stubClient());
104+
const requestPromise = Promise.resolve(createRequest(VerificationPhase.Cancelled));
105+
106+
// When we render the dialog
107+
render(
108+
<VerificationRequestDialog
109+
onFinished={jest.fn()}
110+
member={member}
111+
verificationRequestPromise={requestPromise}
112+
/>,
113+
);
114+
115+
// And wait for the component to mount, the promise to resolve and the component state to update
116+
await act(async () => await new Promise(process.nextTick));
117+
118+
// Then it renders the resolved information
119+
expect(screen.getByRole("heading", { name: "Verify other device" })).toBeInTheDocument();
120+
expect(screen.getByRole("heading", { name: "Verification cancelled" })).toBeInTheDocument();
121+
122+
expect(
123+
screen.getByText(
124+
"You cancelled verification on your other device. Start verification again from the notification.",
125+
),
126+
).toBeInTheDocument();
127+
});
128+
129+
it("Renders the later promise request if both immediate and promise are supplied", async () => {
130+
// Given we supply a promise of a request as well as a request
131+
const member = User.createUser("@alice:example.org", stubClient());
132+
const request = createRequest(VerificationPhase.Ready);
133+
const requestPromise = Promise.resolve(createRequest(VerificationPhase.Cancelled));
134+
135+
// When we render the dialog
136+
render(
137+
<VerificationRequestDialog
138+
onFinished={jest.fn()}
139+
member={member}
140+
verificationRequest={request}
141+
verificationRequestPromise={requestPromise}
142+
/>,
143+
);
144+
145+
// And wait for the component to mount, the promise to resolve and the component state to update
146+
await act(async () => await new Promise(process.nextTick));
147+
148+
// Then it renders the information from the request in the promise
149+
expect(screen.getByRole("heading", { name: "Verify other device" })).toBeInTheDocument();
150+
expect(screen.getByRole("heading", { name: "Verification cancelled" })).toBeInTheDocument();
151+
152+
expect(
153+
screen.getByText(
154+
"You cancelled verification on your other device. Start verification again from the notification.",
155+
),
156+
).toBeInTheDocument();
157+
});
158+
});
159+
160+
function createRequest(phase: VerificationPhase, method?: "emoji" | "qr"): VerificationRequest {
161+
let verifier = undefined;
162+
let chosenMethod = undefined;
163+
164+
switch (method) {
165+
case "emoji":
166+
chosenMethod = VerificationMethod.Sas;
167+
verifier = createEmojiVerifier();
168+
break;
169+
case "qr":
170+
chosenMethod = VerificationMethod.Reciprocate;
171+
verifier = createQrVerifier();
172+
break;
173+
}
174+
175+
return {
176+
phase: jest.fn().mockReturnValue(phase),
177+
178+
// VerificationRequest is an emitter - ignore any events that are emitted.
179+
on: jest.fn(),
180+
off: jest.fn(),
181+
182+
// These tests (so far) only check for when we are initiating a verificiation of our own device.
183+
isSelfVerification: jest.fn().mockReturnValue(true),
184+
initiatedByMe: jest.fn().mockReturnValue(true),
185+
186+
// Always returning true means we can support QR code and emoji verification.
187+
otherPartySupportsMethod: jest.fn().mockReturnValue(true),
188+
189+
// If we asked for emoji, these are populated.
190+
verifier,
191+
chosenMethod,
192+
} as unknown as VerificationRequest;
193+
}
194+
195+
function createEmojiVerifier(): Verifier {
196+
const showSasCallbacks = {
197+
sas: {
198+
emoji: [
199+
// Example set of emoji to display.
200+
["🐶", "Dog"],
201+
["🐱", "Cat"],
202+
],
203+
},
204+
} as ShowSasCallbacks;
205+
206+
return {
207+
getShowSasCallbacks: jest.fn().mockReturnValue(showSasCallbacks),
208+
getReciprocateQrCodeCallbacks: jest.fn(),
209+
on: jest.fn(),
210+
off: jest.fn(),
211+
verify: jest.fn(),
212+
} as unknown as Verifier;
213+
}
214+
215+
function createQrVerifier(): Verifier {
216+
const reciprocateQrCodeCallbacks = {
217+
confirm: jest.fn(),
218+
cancel: jest.fn(),
219+
} as ShowQrCodeCallbacks;
220+
221+
return {
222+
getShowSasCallbacks: jest.fn(),
223+
getReciprocateQrCodeCallbacks: jest.fn().mockReturnValue(reciprocateQrCodeCallbacks),
224+
on: jest.fn(),
225+
off: jest.fn(),
226+
verify: jest.fn(),
227+
} as unknown as Verifier;
228+
}

0 commit comments

Comments
 (0)