Compare commits

...

186 Commits

Author SHA1 Message Date
Will Hunt
2b8e8b32f2 Merge branch 'develop' into hs/empty-topics 2025-02-03 10:04:19 +00:00
Michael Telatynski
4f1eac67a8 Fix share button in discovery settings being disabled incorrectly (#29151)
* Fix share button in discovery settings being disabled incorrectly

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

* Improve types & add tests

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

* Iterate

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

* Iterate

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

* Improve coverage

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

* Add missing snapshot

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-02-03 08:48:02 +00:00
R Midhun Suresh
aa01b17f9e Always show back button in the right panel (#29128)
* Construct history on setCard

So that back buttons are always shown in the right panel

* Check card state to ensure operation is atomic

* Fix tests

* Fix lint

* Remove null case

* Fix broken test
2025-02-02 18:37:12 +00:00
Hubert Chathi
4cba79ddcc Schedule dehydration on reload if the dehydration key is already cached locally (#29021)
* Schedule dehydration on reload

* fix test and use the right function to check dehydration is enabled

* use dehydration helper function when scheduling dehydration on restart

* fix test by passing in client object
2025-01-31 18:29:59 +00:00
Will Hunt
c781e1db73 Merge branch 'develop' into hs/empty-topics 2025-01-31 17:41:12 +00:00
Half-Shot
3014ee4ad4 Add tests for topic rendering 2025-01-31 17:39:52 +00:00
Half-Shot
a94125b791 Use the correct import pattern 2025-01-31 17:39:47 +00:00
Half-Shot
0d33d2be14 Revert changes i18n for the moment 2025-01-31 17:06:46 +00:00
Michael Telatynski
b64471e4f6 Ensure switching rooms does not wrongly focus timeline search (#29153)
This happened due to the focusRoomSearch param being stored for inactive rooms so it never got cleared

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-31 15:47:32 +00:00
Valere
d3a6f34881 feat(crypto): Support verification violation composer banner (#29067)
* feat(crypto): Support verification violation composer banner

* refactor UserIdentityWarning by using now a ViewModel

fixup: logger import

fixup: test lint type problems

fix test having an unexpected verification violation

fixup sonarcubes warnings

* review: comments on types and inline some const

* review: Quick refactor, better handling of action on button click

* review: Small updates, remove commented code
2025-01-31 15:05:32 +00:00
ElementRobot
dcce9c70dc Localazy Download (#29149)
* [create-pull-request] automated change

* Fix test

---------

Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
Co-authored-by: R Midhun Suresh <hi@midhun.dev>
2025-01-31 12:30:53 +00:00
Half-Shot
ee7ff697cb Use topic parser for topic events. 2025-01-31 12:24:23 +00:00
Half-Shot
718dccd589 Improve display of empty topic events in the timeline. 2025-01-31 12:22:15 +00:00
ElementRobot
f06ed2fa1f [create-pull-request] automated change (#29148)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2025-01-31 06:14:35 +00:00
Richard van der Hoff
099c3073b6 Stop showing a dialog prompting the user to enter an old recovery key (#29143)
* SecurityManager: improve logging

* Only prompt user for default 4S key

We don't really support the concept of having multiple 4S keys active, so
prompting the user to enter a non-default 4S key without even telling them
which one we want is rather silly.

* playwright: factor out helper for setting up 4S

We seem to already have about 5 copies of this code, so before I add another,
let's factor it out.

* Playwright test for dehydrated device in reset flow

This should be fixed by the previous commit, so let's check it stays that way.
2025-01-30 16:27:45 +00:00
ElementRobot
12932e2dc6 [create-pull-request] automated change (#29144)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2025-01-30 09:22:24 +00:00
David Langley
a7de29429c Bump emojibase_bindings to include emoji 15.1 (#29132) 2025-01-29 17:42:22 +00:00
Florian Duros
d3ea250d77 Remove call to MatrixClient.setGlobalErrorOnUnknownDevices (#29134)
* refactor(MatrixChat): remove `MatrixClient.setGlobalErrorOnUnknownDevices` call

MatrixClient.setGlobalErrorOnUnknownDevices is not implemented in the rust-crypto and will be removed when the legacy crypto will be ripped out.

* test(e2e): remove `MatrixClient.setGlobalErrorOnUnknownDevices` call

MatrixClient.setGlobalErrorOnUnknownDevices is not implemented in the rust-crypto and will be removed when the legacy crypto will be ripped out.
2025-01-29 17:24:44 +00:00
ElementRobot
f243fee5a6 [create-pull-request] automated change (#29122)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2025-01-29 06:18:59 +00:00
ElementRobot
296d0074ed [create-pull-request] automated change (#29121)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2025-01-29 06:14:16 +00:00
RiotRobot
df83338f26 Reset matrix-js-sdk back to develop branch 2025-01-28 13:36:28 +00:00
RiotRobot
c0336f21f6 Merge branch 'master' into develop 2025-01-28 13:36:12 +00:00
RiotRobot
d88f47bdbc v1.11.91 2025-01-28 13:33:06 +00:00
RiotRobot
4a26414957 Upgrade dependency to matrix-js-sdk@36.1.0 2025-01-28 13:23:15 +00:00
Matthew Hodgson
886d0e1241 update to twemoji 15.1.0 (#29115) 2025-01-28 11:15:33 +00:00
Robin
c453d33456 Make themed widgets reflect the effective theme (#28342)
* Make themed widgets reflect the effective theme

So that widgets such as Element Call will show up in the right theme even if the app is set to match the system theme.

* Remove debug log line
2025-01-28 10:53:35 +00:00
ElementRobot
ddf221b813 [create-pull-request] automated change (#29114)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2025-01-28 06:14:17 +00:00
Timo
08238bb883 Update matrix-widget-api (#29112)
This fixes element call not loading with the `update_state` refactor: https://github.com/element-hq/element-web/pull/28681
2025-01-27 17:46:39 +00:00
Michael Telatynski
c390ec333e Tidy up modules (#29089)
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-27 17:27:27 +00:00
Michael Telatynski
3c22e5dc68 Switch to mailpit for Playwright tests (#29108)
* Switch to mailpit for Playwright tests

as mailhog is unsupported and lacks arm64 support

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

* Fix yarn.lock

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

* Iterate

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

* Iterate

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

* delint

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

* Iterate

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-27 15:35:06 +00:00
R Midhun Suresh
f29ce94dd4 Style room header icons and facepile for toggled state (#28968)
* Fix tiny typo in existing code

* Create a hook that uses the right panel store

So that we track changes to the right panel phases

* Create a context that wraps the previous hook we created

We do this so that we can get by using a single event listener i.e we
only need to call `useCurrentPhase` in the provider as opposed to
calling it in each header icon.

* Create a hook that tells you if a panel is open or not

* Create component that wraps Icon

and adds a class name when the corresponding panel is open

* Style room header icons for when they are toggled

* Style face pile for toggle state

* Fix broken CI

* Give directory a better name

* Update year in license

* Use a stronger type
2025-01-27 15:05:46 +00:00
R Midhun Suresh
76485cfb17 Allow navigating through the memberlist using up/down keys (#28949)
* Allow flex component to take child containers props

So that we can set attributes on the container

* Use Up/Down arrow keys to navigate through the list

* Update snapshot
2025-01-27 15:05:28 +00:00
Matthew Hodgson
c0567fc5f4 support non-VS16 emoji ligatures in TwemojiMozilla (#29100)
to workaround broken ligature support for VS16 ligature colour emoji in Chrome 131+
See https://github.com/element-hq/element-web/issues/28500
2025-01-27 14:54:13 +00:00
R Midhun Suresh
95da3834f2 Move threads header below base card header (#28969)
* Move threads header below base card header

* Fix jest tests
2025-01-27 11:10:25 +00:00
ElementRobot
b8a3468485 [create-pull-request] automated change (#29025)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2025-01-27 08:51:33 +00:00
ElementRobot
b7f6e0f88c [create-pull-request] automated change (#29092)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2025-01-25 06:13:48 +00:00
Florian Duros
b36d9ce32e test(e2e): verify session with the encryption tab instead of the security & privacy tab (#29090) 2025-01-24 15:03:59 +00:00
Michael Telatynski
9b6be0f5a9 Tidy up unused classes & icons in UserMenu styles (#29087)
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-24 14:52:24 +00:00
David Baker
cae0da8f00 Fix prettier 2025-01-24 14:23:39 +00:00
David Baker
25689de34a Attempt 3 at working around R2 brokenness 2025-01-24 14:07:55 +00:00
Michael Telatynski
ce6cb47943 Attempt 2 at working around aws-cli & r2 incompatibility 2025-01-24 13:46:02 +00:00
Florian Duros
850c1a5b3a e2e test: Check key backup with js-sdk api instead of relying of Security & Privacy tab (#29066)
* test(e2e): `checkDeviceIsConnectedKeyBackup` is checking the key backup with the matrix client and the crypto api instead of relying of the `Security & Privacy` tab.

* test(e2e): renaming and improve documentation
2025-01-24 12:34:22 +00:00
David Baker
ec4ae9e58a Work around cloudflare R2 / aws client incompatability (#29086)
Hopefully...

https://www.cloudflarestatus.com/incidents/t5nrjmpxc1cj
2025-01-24 12:20:22 +00:00
Florian Duros
a73eb378d7 Add cryptography information in devtools (#29073)
* feat(devtools): add crypto information in devtools

* ci: add crypto devtools file to crypto code owners

* test(dev tools): update test to add new crypto button

* test(dev tools): add tests for crypto component
2025-01-24 10:51:27 +00:00
David Baker
197afd6a9e Fix identity server settings visibility (#29083)
* Fix identity server settings visibility

The IS settings got confused with the posthog settings and were only
shown if analytics were enabled.

* Update snapshot
2025-01-24 10:07:26 +00:00
Florian Duros
ac565dca80 Add Advanced section to the user settings encryption tab (#28804)
* Make the encryption card more configurable:
- Change the icon
- Can set the destructive props

* Update compound

* Add advanced section

* Add the `Never send encrypted messages to unverified devices` settings

* - Add commercial license
- Remove generic type

* Rename EncryptionDetails css classes

* Use same uiAuthCallback

* Use h3 for title

* Add tests to `AdvancedPanel`

* Add tests to `EncryptionUserSettingsTab`

* Add tests to `ResetIdentityPanel`

* Get only the recovery section in recovery tests

* Add e2e test
2025-01-24 08:33:16 +00:00
renovate[bot]
a0044d6b5f Update testcontainers-node monorepo to v10.17.1 (#29053)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-23 00:03:47 +00:00
taffyko
68c03db557 Fix outstanding UX issues with replies/mentions/keyword notifs (#28270)
* Fix outstanding UX issues with replies/mentions/keyword notifs

* Use createRoot instead of deprecated ReactDOM.render

I foresee this change being made across the codebase shortly
and want to proactively prevent my PR from falling behind

* Clean up react root on unmount

* Remove addition of left-edge highlight on message mentions

It is clear that it would be best for me to address
this piece in a separate PR.

* Update call to ReactRootManager.render

---------

Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-22 21:42:40 +00:00
Florian Duros
9a109cdce8 test: remove unused toast screenshot (#29074) 2025-01-22 18:47:49 +00:00
Robin
a0ab88943b Distinguish room state and timeline events when dealing with widgets (#28681)
* Distinguish room state and timeline events when dealing with widgets

* Upgrade matrix-widget-api

* Fix typo

* Fix tests

* Write more tests

* Add more comments

---------

Co-authored-by: Hugh Nimmo-Smith <hughns@users.noreply.github.com>
2025-01-22 17:50:52 +00:00
Michael Telatynski
ad01218942 Switch OIDC primarily to new /auth_metadata API (#29019)
* Switch OIDC primarily to new `/auth_metadata` API

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

* Update tests

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

* Iterate

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

* Simplify the world

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

* Iterate

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-22 13:48:28 +00:00
R Midhun Suresh
e1e4d26154 More memberlist changes (#29069)
* Remove parenthesis from Invited user label

* Ensure adequate margin

* Truncate user id with ellipsis

* Fix tests
2025-01-22 13:31:47 +00:00
Michael Telatynski
84c614676d Improve release documentation for staging.element.io deployments (#29038)
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-22 09:11:35 +00:00
ElementRobot
29d9e98111 [create-pull-request] automated change (#29063)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2025-01-22 06:14:32 +00:00
renovate[bot]
9f5f898ed8 Update all non-major dependencies (#29046)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-21 19:41:52 +00:00
renovate[bot]
78251a3a8a Update dependency @types/node to v18.19.71 (#29042)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-21 19:16:57 +00:00
renovate[bot]
1b077c53f5 Update dependency @sentry/browser to v8.50.0 (#29048)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-21 18:48:22 +00:00
renovate[bot]
68828a2326 Update stylelint (#29052)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-21 18:33:50 +00:00
renovate[bot]
af8d93f58a Update typescript-eslint monorepo to v8.20.0 (#29055)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-21 18:27:19 +00:00
renovate[bot]
c0a097867e Update babel monorepo (#29047)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-21 18:11:26 +00:00
renovate[bot]
0b13e57518 Update docker/build-push-action digest to 67a2d40 (#29039)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-21 18:05:56 +00:00
renovate[bot]
8615b411b2 Update browserslist (#29041)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-21 18:05:18 +00:00
renovate[bot]
3d31376b1d Update guibranco/github-status-action-v2 digest to ecd54a0 (#29040)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-21 17:50:02 +00:00
renovate[bot]
43e5124cd4 Update matrix-org (#29051)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-21 17:42:33 +00:00
renovate[bot]
19674cca08 Update dependency @sentry/webpack-plugin to v3 (#29056)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-21 17:39:34 +00:00
renovate[bot]
6ca6cb0fbe Update testing-library monorepo (#29054)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-21 17:37:10 +00:00
renovate[bot]
d92fc5a595 Update dependency @types/react-transition-group to v4.4.12 (#29043)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-21 17:36:43 +00:00
renovate[bot]
b9d411eecc Update dependency typescript to v5.7.3 (#29044)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-21 17:36:26 +00:00
renovate[bot]
3da6619bcf Update dependency @stylistic/eslint-plugin to v2.13.0 (#29049)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-21 17:34:38 +00:00
renovate[bot]
f33e7c9782 Update fontsource monorepo to v5.1.1 (#29045)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-21 17:34:23 +00:00
renovate[bot]
1ebae09834 Update dependency eslint-config-prettier to v10 (#29057)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-21 17:33:17 +00:00
RiotRobot
790a976421 v1.11.91-rc.1 2025-01-21 15:34:13 +00:00
ElementRobot
1e1d66924f Switch to secure random strings (#29013) (#29035)
* Switch to secure random strings

Because the js-sdk methods are changing and there's no reason for these
not to use the secure versions. The dedicated upper/lower functions were
*only* used in this one case, so this should do the exact same thing with
the one exported function.

Requires https://github.com/matrix-org/matrix-js-sdk/pull/4621 (merge both together)

* Change remaining instances of randomString

which I somehow entirely missed the first time.

* Fix import order

(cherry picked from commit 56eafc908e)

Co-authored-by: David Baker <dbkr@users.noreply.github.com>
2025-01-21 15:15:44 +00:00
RiotRobot
63ecb48d7d v1.11.91-rc.0 2025-01-21 14:48:40 +00:00
RiotRobot
5e3fc8aa19 Upgrade dependency to matrix-js-sdk@36.1.0-rc.0 2025-01-21 14:42:45 +00:00
David Baker
56eafc908e Switch to secure random strings (#29013)
* Switch to secure random strings

Because the js-sdk methods are changing and there's no reason for these
not to use the secure versions. The dedicated upper/lower functions were
*only* used in this one case, so this should do the exact same thing with
the one exported function.

Requires https://github.com/matrix-org/matrix-js-sdk/pull/4621 (merge both together)

* Change remaining instances of randomString

which I somehow entirely missed the first time.

* Fix import order
2025-01-21 13:54:57 +00:00
R Midhun Suresh
1644169ff3 Implement changes to memberlist from feedback (#29029)
* Add a separator between joined and invited members

* Fix user label in tile having wrong color

* Changes to member tiles

- ThreePidInviteTile now contains an user label showing "(Invited)" and
  an email icon.
- RoomMemberTile now includes an icon similar to above.
- Refactors a bunch of code to make this change sensible.

* Remove redundant css code

* Fix tests

* Update src/components/viewmodels/memberlist/MemberListViewModel.tsx

Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>

* Update year in license

* Fix lint error

---------

Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-21 11:01:32 +00:00
Michael Telatynski
cf895b4296 Specify branch to match CFP production branch 2025-01-21 10:39:47 +00:00
ElementRobot
e9d4f39e9d [create-pull-request] automated change (#29032)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-21 09:50:22 +00:00
Michael Telatynski
7c0ec21365 Enable fixed pinecone test (#29027)
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-21 09:06:11 +00:00
renovate[bot]
72df9c9076 Update dependency katex to v0.16.21 [SECURITY] (#29022)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 09:40:10 +00:00
Michael Telatynski
e42ee727b4 Fix more flaky playwright tests (#29007)
* Group systemic playwright flakes

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

* Fix more flaky tests

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

* Fix another flake

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

* Iterate

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

* Iterate

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

* delint

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

* Fix more flakes

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

* Iterate

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

* Fix skip tests being wrong

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-17 11:08:49 +00:00
ElementRobot
7d30413178 [create-pull-request] automated change (#29016)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2025-01-17 06:18:36 +00:00
ElementRobot
8884e77ce3 [create-pull-request] automated change (#29015)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2025-01-17 06:13:54 +00:00
David Baker
58f812ffe6 Add toast for recovery keys being out of sync (#28946)
* Refine `SettingsSection` & `SettingsTab`

* Add encryption tab

* Add recovery section

* Add device verification

* Rename `Panel` into `State`

* Update & add tests to user settings common

* Add tests to `RecoveryPanel`

* Add tests to `ChangeRecoveryKey`

* Update CreateSecretStorageDialog-test snapshot

* Add tests to `EncryptionUserSettingsTab`

* Update existing screenshots of e2e tests

* Add new encryption tab ownership to `@element-hq/element-crypto-web-reviewers`

* Add e2e tests

* Fix monospace font and add figma link to hardcoded value

* Add unit to Icon

* Improve e2e doc

* Assert that the crypto module is defined

* Add classname doc

* Fix typo

* Use `good` state instead of default

* Rename `ChangeRecoveryKey.isSetupFlow` into `ChangeRecoveryKey.userHasKeyBackup`

* Move `deleteCachedSecrets` fixture in `recovery.spec.ts`

* Use one callback instead of two in `RecoveryPanel`

* Fix docs and naming of `utils.createBot`

* Fix typo in `RecoveryPanel`

* Add more doc to the state of the `EncryptionUserSettingsTab`

* Rename `verification_required` into `set_up_encryption`

* Update test

* ADd new license

* Very early WIP of rejigged e2e error toast code

* Update comments and doc

* Assert that `recoveryKey.encodedPrivateKey` is always defined

* Add comments to explain how the secrets could be uncached

* Use `matrixClient.secretStorage.getDefaultKeyId` instead of `matrixClient.getCrypto().checkKeyBackupAndEnable` to know if we need to set up a recovery key

* Update existing screenshot to add encryption tab.

* Fix tests

* Remove unused file!

* Remove test for unused file

* Show 'set up encryption' in the 'other' case.

* Test 'key storage out of sync' toast

* Update tests

* Fix test & make toast look correct

* Use new labels when changing the recovery key

* Fix docs

* Don't reset key backup when creating a recovery key

* Add playwright test for toast

* Dismiss the toast as it's now in the way due to being wider

* Doesn't look like this needs to be async

* Typo

Co-authored-by: Andy Balaam <andy.balaam@matrix.org>

* Typo

Co-authored-by: Andy Balaam <andy.balaam@matrix.org>

* Override width for just this toast

---------

Co-authored-by: Florian Duros <florian.duros@ormaz.fr>
Co-authored-by: Florian Duros <florianduros@element.io>
Co-authored-by: Andy Balaam <andy.balaam@matrix.org>
2025-01-16 13:17:18 +00:00
Michael Telatynski
ef1597ff2d Enable react-compiler eslint to spot antipatterns (#28652)
* Switch to React18 useId

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

* Enable react-compiler eslint

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

* Fix an easy one

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

* Disable in tests

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

* Fix usage of useRef as memoization

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

* Fix mutation of external values in hooks

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

* Make React compiler happy about some frankly non-issues

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

* Fix MapMock

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

* Iterate

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

* Iterate

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

* Iterate

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

* Revert MemberListViewModel.tsx changes and disable linter per line

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

* Make viewmodel compatible with react-compiler linter

- Remove searchQuery ref/state and instead pass this query to the
  loadMember function.
- Now we no longer need a separate search function

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
Co-authored-by: R Midhun Suresh <hi@midhun.dev>
2025-01-16 12:26:00 +00:00
Michael Telatynski
e5ca7954c8 Refactor LegacyCallHandler event emitter to use TypedEventEmitter (#29008)
* Switch LegacyCallHandler over to TypedEventEmitter and use emits to notify consumers of protocol support updates

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

* Add test for dialpad

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-16 10:44:20 +00:00
Florian Duros
13913ba8b2 Add Recovery section in the new user settings Encryption tab (#28673)
* Refine `SettingsSection` & `SettingsTab`

* Add encryption tab

* Add recovery section

* Add device verification

* Rename `Panel` into `State`

* Update & add tests to user settings common

* Add tests to `RecoveryPanel`

* Add tests to `ChangeRecoveryKey`

* Update CreateSecretStorageDialog-test snapshot

* Add tests to `EncryptionUserSettingsTab`

* Update existing screenshots of e2e tests

* Add new encryption tab ownership to `@element-hq/element-crypto-web-reviewers`

* Add e2e tests

* Fix monospace font and add figma link to hardcoded value

* Add unit to Icon

* Improve e2e doc

* Assert that the crypto module is defined

* Add classname doc

* Fix typo

* Use `good` state instead of default

* Rename `ChangeRecoveryKey.isSetupFlow` into `ChangeRecoveryKey.userHasKeyBackup`

* Move `deleteCachedSecrets` fixture in `recovery.spec.ts`

* Use one callback instead of two in `RecoveryPanel`

* Fix docs and naming of `utils.createBot`

* Fix typo in `RecoveryPanel`

* Add more doc to the state of the `EncryptionUserSettingsTab`

* Rename `verification_required` into `set_up_encryption`

* Update test

* ADd new license

* Update comments and doc

* Assert that `recoveryKey.encodedPrivateKey` is always defined

* Add comments to explain how the secrets could be uncached

* Use `matrixClient.secretStorage.getDefaultKeyId` instead of `matrixClient.getCrypto().checkKeyBackupAndEnable` to know if we need to set up a recovery key

* Update existing screenshot to add encryption tab.

* Update tests

* Use new labels when changing the recovery key

* Fix docs

* Don't reset key backup when creating a recovery key

* Fix doc
2025-01-15 12:44:20 +00:00
Michael Telatynski
03a1b48e1f Retry loading chunks to make the app more resilient (#29001)
Will also fix some Playwright flakes

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-15 09:57:07 +00:00
ElementRobot
ef3bf59656 [create-pull-request] automated change (#29003)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2025-01-15 06:18:30 +00:00
ElementRobot
7a3202b537 [create-pull-request] automated change (#29002)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2025-01-15 06:13:41 +00:00
Michael Telatynski
dbce48b23d Run Playwright tests against Dendrite & Pinecone periodically (#28888)
* Switch to TestContainers for manging services in Playwright

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

* Iterate

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

* Flip fixture dependency order

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

* Remove mas dep

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Update matrix-authentication-service in Playwright tests

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

* delint

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

* Fix SMTP port

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

* Iterate

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

* Comments

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

* Strip ansi from playwright logs to make them more readable

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

* Actually do the update

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

* Iterate

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

* Remove access to homeserver.config.baseUrl field in favour of homeserver.baseUrl

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

* Use sane default_server_config and specify server.invalid in the specific tests which demand it

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

* Fix mas run

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

* break cycle

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

* Iterate

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

* typo

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

* Iterate

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

* Iterate

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

* Iterate

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

* prettier

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

* Wire up basics of dendriteHomeserver

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

* Run Playwright tests against Dendrite & Pinecone periodically

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Fix playwright-image-updates.yaml workflow

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

* Add `X-Run-All-Tests` label for running all tests

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

* Ignore failing tests in stale-screenshot-reporter.ts to avoid confusing errors

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

* Iterate

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

* Iterate

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

* Specify Synapse ui_auth.session_timeout only on tests which require it

As Dendrite lacks this configuration option

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* React to MatrixEvent sender/target sentinels being updated for rendering state events

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

* Iterate

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

* Iterate

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

* Remove test code

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

* Iterate

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

* React to sentinel changes in EventListSummary

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

* Iterate

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

* Iterate

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

* Docs

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

* Avoid reusing user1234

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

* Fix stale-screenshot-reporter.ts

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

* Clean up public rooms between tests on reused homeserver

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

* Deflake spotlight when homeserver is reused

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

* Deflake more tests using existing username

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

* Clean mailhog between tests

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

* Fix more flakes

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

* Fix missing _request

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Fix playwright flaky tests

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

* Wipe mailhog between test runs

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

* Delint

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

* Iterate

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

* delint

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

* Deflake more tests

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

* Iterate

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

* Fix flaky tests

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

* Fix flaky tests

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

* Fix mas config

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

* Fix another flaky test

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

* Fix playwright flakes due to floating promises

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

* Iterate

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

* Iterate

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

* Fix flaky playwright tests

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Update services.ts

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-14 18:00:30 +00:00
RiotRobot
bb41616d5f Reset matrix-js-sdk back to develop branch 2025-01-14 14:18:55 +00:00
RiotRobot
c75f6dc3a1 Merge branch 'master' into develop 2025-01-14 14:18:36 +00:00
RiotRobot
880048d998 v1.11.90 2025-01-14 14:13:47 +00:00
RiotRobot
24685dc7d1 Upgrade dependency to matrix-js-sdk@36.0.0 2025-01-14 14:10:44 +00:00
Michael Telatynski
60f70b93e0 Remove FTUE onboarding as it is incompatible with SSO/OIDC (#28943)
* Remove FTUE onboarding as it is incompatible with SSO/OIDC

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

* Update tests

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

* Remove stale screenshots

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-14 13:35:20 +00:00
Michael Telatynski
2559cba482 Clear account idb table on logout (#28996)
* Clear account idb table on logout

to remove old deactivated refresh token when logging out

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

* Simplify code

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

* Fix test

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-14 12:01:19 +00:00
Michael Telatynski
5882b004f5 Fix flaky playwright tests (#28957)
* Docs

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

* Avoid reusing user1234

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

* Fix stale-screenshot-reporter.ts

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

* Clean up public rooms between tests on reused homeserver

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

* Deflake spotlight when homeserver is reused

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

* Deflake more tests using existing username

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

* Clean mailhog between tests

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

* Fix more flakes

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

* Fix missing _request

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Fix playwright flaky tests

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

* Wipe mailhog between test runs

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

* Delint

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

* Iterate

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

* delint

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

* Deflake more tests

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

* Iterate

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

* Fix flaky tests

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

* Fix flaky tests

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

* Fix mas config

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

* Fix another flaky test

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-14 11:37:39 +00:00
Michael Telatynski
37f8d70d89 Fix flaky playwright tests (#28989)
* Fix flaky playwright tests

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

* Fix flaky test

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

* Iterate

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

* Iterate

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

* Add slow

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-14 09:58:32 +00:00
Michael Telatynski
e2bd040c88 Simplify playwright (#28988)
* Simplify types

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

* Fix typos

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-14 08:58:06 +00:00
ElementRobot
381b2ea343 [create-pull-request] automated change (#28992)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2025-01-14 06:16:25 +00:00
Michael Telatynski
41944e5c6e Update flaky-reporter.ts 2025-01-13 18:27:31 +00:00
Michael Telatynski
540580504d Fix flaky playwright tests (#28984)
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-13 17:43:28 +00:00
Michael Telatynski
1a21b718d8 Fix playwright flakes due to floating promises (#28981)
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-13 17:29:22 +00:00
Michael Telatynski
2cddb16a9f Fix flaky playwright tests (#28975)
* Fix flaky tests

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

* Fix flaky tests

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

* Fix mas config

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

* Fix another flaky test

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-13 17:26:01 +00:00
R Midhun Suresh
671d6de805 Merge identical enums (#28889) 2025-01-13 16:47:52 +00:00
Michael Telatynski
0f8a2e93ce Enable previously disabled Playwright tests (#28976)
* Enable previously disabled Playwright tests

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

* Iterate

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

* Iterate

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

* Add screenshots

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-13 16:27:17 +00:00
Michael Telatynski
bff2d680e6 Speed up Netlify further (#28978)
* Speed up Netlify further

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

* Update build.yml

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-13 16:27:13 +00:00
Michael Telatynski
5a5db19c2c Add flaky test labels for playwright projects (#28980)
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-13 16:27:10 +00:00
Michael Telatynski
11a8723c73 Playwright: get console logs without trace (#28972)
* Playwright: get console logs without trace

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

* Add page url to log

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

* Skip empty logs

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

* Iterate

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

* Reset page counter

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-13 16:15:13 +00:00
Michael Telatynski
e14a3b64c3 Fix flaky playwright tests (#28959)
* Fix playwright flaky tests

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

* Wipe mailhog between test runs

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

* Delint

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

* Iterate

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-13 09:32:00 +00:00
Michael Telatynski
f99d7ce2bb React to MatrixEvent sender/target being updated for rendering state events (#28947)
* React to MatrixEvent sender/target sentinels being updated for rendering state events

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

* Iterate

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

* Iterate

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

* React to sentinel changes in EventListSummary

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

* Iterate

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

* Iterate

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-10 10:44:10 +00:00
Michael Telatynski
585aa75525 Remove ts-prune as it has been archived over a year ago (#28954)
* Remove ts-prune as it has been archived over a year ago

knip replaces it has better configuration

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

* Update knip config

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-10 09:58:07 +00:00
ElementRobot
effef7eaa7 [create-pull-request] automated change (#28955)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2025-01-10 06:22:16 +00:00
ElementRobot
9826a8851d [create-pull-request] automated change (#28867)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2025-01-09 09:09:33 +00:00
R Midhun Suresh
ebef0d353e Implement new memberlist design with MVVM architecture (#28874)
* Add new e2e icon for the member tile

* Add new presence icon for member tile

* Implement new member tile

* Implement memberlist view model

* Implement new memberlist header view

* Support the new memberlist in Diasambiguated profile

1. Use MemberInfo instead of RoomMember
2. CSS changes to reflect the new design

* Implement new memberlist view

* Add and use a new overflow component

We used the EntityTile component as a pretend overflow tile in some
places. This new lighter component is added so  that we can remove the
complex EntityTile component.

* Remove old code

* Add/remove css files from _components.pcss

* Increase minimum width as per design

* Actually use the new memberlist view

* Fix broken jest tests

* Add jest tests

* Playwright: Make it possible to disable presence

* Add playwright tests

* Fix lint error

* Undo translation changes that must be done via localazy

* Update license header

* Use waitFor instead of setTimeout

* Remove comment

* Switch over from template to container hs

* Revert unintended change

* Move config to top level
2025-01-08 17:15:06 +00:00
Michael Telatynski
f1899b9eb1 Specify Synapse ui_auth.session_timeout only on tests which require it (#28939)
* Specify Synapse ui_auth.session_timeout only on tests which require it

As Dendrite lacks this configuration option

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

* Iterate

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-08 15:53:19 +00:00
renovate[bot]
027891a35a Update css (#27948)
* Update css

* Hold back postcss somewhat

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-08 15:48:17 +00:00
Michael Telatynski
2f7c28ded0 Improve playwright docs (#28944)
* Improve playwright docs

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

* Update docs/playwright.md

Co-authored-by: R Midhun Suresh <hi@midhun.dev>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
Co-authored-by: R Midhun Suresh <hi@midhun.dev>
2025-01-08 15:39:08 +00:00
renovate[bot]
b6aba1477b Update docker (#28927)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-08 15:01:27 +00:00
renovate[bot]
056ecbb138 Update all non-major dependencies (#28928)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-08 15:00:31 +00:00
renovate[bot]
7685e547de Update dependency stylelint to v16.12.0 (#28936)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-08 14:03:14 +00:00
renovate[bot]
a0a4211447 Update dependency @types/react-transition-group to v4.4.12 (#28931)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-08 13:45:31 +00:00
renovate[bot]
0ad4e13e2d Update dependency browserslist to v4.24.3 (#28932)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-08 13:45:17 +00:00
renovate[bot]
f406305510 Update fontsource monorepo to v5.1.1 (#28933)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-08 13:45:13 +00:00
renovate[bot]
6cb174e3d9 Update dependency @matrix-org/spec to v1.13.0 (#28934)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-08 13:44:36 +00:00
renovate[bot]
c569478240 Update dependency @sentry/browser to v8.47.0 (#28935)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-08 13:44:21 +00:00
renovate[bot]
2bd8e049c7 Update definitelyTyped (#28929)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-08 13:43:52 +00:00
renovate[bot]
e8d69dc592 Update dependency @formatjs/intl-segmenter to v11.7.8 (#28930)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-08 13:43:40 +00:00
Michael Telatynski
50ac509a01 Use homeserver in a worker-scoped fixture (#28848)
* Use homeserver in a worker-scoped fixture

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

* Iterate

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

* Iterate

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

* Switch to TestContainers for manging services in Playwright

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

* Iterate

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

* Flip fixture dependency order

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

* Remove mas dep

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Update matrix-authentication-service in Playwright tests

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

* delint

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

* Fix SMTP port

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

* Iterate

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

* Comments

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

* Strip ansi from playwright logs to make them more readable

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

* Actually do the update

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

* Iterate

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

* Remove access to homeserver.config.baseUrl field in favour of homeserver.baseUrl

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

* Use sane default_server_config and specify server.invalid in the specific tests which demand it

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

* Fix mas run

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

* break cycle

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

* Iterate

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

* typo

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

* Iterate

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

* Iterate

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

* Iterate

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

* prettier

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

* Wire up basics of dendriteHomeserver

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

* Iterate

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

* Iterate

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

* Iterate

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

* Fix types

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

* Iterate

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

* Iterate

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

* Discard changes to playwright/e2e/settings/device-management.spec.ts

* Iterate

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

* Fix bad merge

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-08 12:48:48 +00:00
Michael Telatynski
3e27a0019d Playwright fixes and improvements (#28925)
* Fix playwright-image-updates.yaml workflow

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

* Add `X-Run-All-Tests` label for running all tests

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

* Ignore failing tests in stale-screenshot-reporter.ts to avoid confusing errors

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-08 12:37:11 +00:00
Michael Telatynski
5caad70191 Stabilise sliding sync playwright test (#28879)
* Stabilise sliding sync playwright test

There's no guarantee that the unsubscription & subscription occur in the same request given the add/remove APIs are async

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

* Stabilise more sliding sync tests

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-08 10:52:01 +00:00
ElementRobot
6846679d34 [create-pull-request] automated change (#28920)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2025-01-08 09:04:20 +00:00
renovate[bot]
7e5420100a Update all non-major dependencies (#28898)
* Update all non-major dependencies

* Fix types

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-08 08:57:24 +00:00
Michael Telatynski
f75d1f5a5e Switch to TestContainers for manging services in Playwright (#28860)
* Switch to TestContainers for manging services in Playwright

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

* Iterate

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

* Flip fixture dependency order

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

* Remove mas dep

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Update matrix-authentication-service in Playwright tests

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

* delint

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

* Fix SMTP port

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

* Iterate

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

* Comments

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

* Strip ansi from playwright logs to make them more readable

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

* Actually do the update

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

* Iterate

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

* Remove access to homeserver.config.baseUrl field in favour of homeserver.baseUrl

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

* Use sane default_server_config and specify server.invalid in the specific tests which demand it

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

* Fix mas run

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

* break cycle

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

* Iterate

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

* typo

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

* Iterate

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

* Iterate

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

* Iterate

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

* prettier

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

* Wire up basics of dendriteHomeserver

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-07 18:11:44 +00:00
renovate[bot]
66bbb84e56 Update typescript-eslint monorepo to v8.19.0 (#28909)
* Update typescript-eslint monorepo to v8.19.0

* lint

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

* Fix bad merge

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-07 17:23:16 +00:00
renovate[bot]
48152d2cd1 Update docker/setup-buildx-action digest to 6524bf6 (#28895)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-07 16:22:39 +00:00
renovate[bot]
0d50e34763 Update dependency webpack-cli to v6 (#28913)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-07 15:45:08 +00:00
renovate[bot]
f157c90ba9 Update definitelyTyped (#28899)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-07 15:13:10 +00:00
renovate[bot]
cccb847d4e Update dependency @sentry/browser to v8.47.0 (#28903)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-07 15:08:44 +00:00
renovate[bot]
a5b739c45a Update dependency stylelint to v16.12.0 (#28907)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-07 15:06:04 +00:00
renovate[bot]
9b1e165e6c Update dependency maplibre-gl to v5 (#28912)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-07 14:56:58 +00:00
renovate[bot]
0e2b16abf1 Update react-types (#28707)
* Update react-types

* resolutions

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-07 14:36:13 +00:00
renovate[bot]
f6e999c87d Update guibranco/github-status-action-v2 digest to 56cd38c (#28896)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-07 14:30:27 +00:00
renovate[bot]
3983bd5646 Update peter-evans/create-pull-request digest to 67ccf78 (#28897)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-07 14:30:16 +00:00
renovate[bot]
e8402f1657 Update browserslist (#28705)
* Update browserslist

* Update ua strings

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-07 14:29:33 +00:00
renovate[bot]
69237e7df2 Update dependency @stylistic/eslint-plugin to v2.12.1 (#28904)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-07 14:13:08 +00:00
renovate[bot]
cf1c0805f1 Update dependency @matrix-org/spec to v1.13.0 (#28902)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-07 14:11:50 +00:00
renovate[bot]
b6a1aea825 Update fontsource monorepo to v5.1.1 (#28901)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-07 14:11:39 +00:00
renovate[bot]
b97005c182 Update dependency @formatjs/intl-segmenter to v11.7.8 (#28900)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-07 14:11:01 +00:00
Michael Telatynski
9599c57a20 Update deploy.yml 2025-01-07 13:00:00 +00:00
RiotRobot
3eb3b936d9 v1.11.90-rc.0 2025-01-07 12:48:01 +00:00
RiotRobot
b488155910 Upgrade dependency to matrix-js-sdk@36.0.0-rc.0 2025-01-07 12:44:53 +00:00
Michael Telatynski
afc0dd5f86 Update deploy.yml 2025-01-07 12:36:53 +00:00
Michael Telatynski
7acadc29cc Speed up Netlify CI/CD (#28878)
* Speed up Netlify CI/CD

By switching it from using the webapp from the E2E CI to the Build CI which doesn't need to await any tests

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

* Iterate

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

* Iterate

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-07 10:58:29 +00:00
Michael Telatynski
a73faffe37 Prepare for switch to testcontainers in playwright (#28880)
* Remove access to homeserver.config.baseUrl field in favour of homeserver.baseUrl

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

* Use sane default_server_config and specify server.invalid in the specific tests which demand it

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

* break cycle

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

* Iterate

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

* typo

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

* Iterate

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

* Iterate

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-07 09:21:07 +00:00
Michael Telatynski
1e758cacae Update triage-stale-flaky-tests.yml 2025-01-07 08:51:27 +00:00
David Baker
7b565e7997 Fix building the automations mermaid diagram (#28881)
* Fix building the automations mermaid diagram

The was using a runner output instead which is not going to work in
this simple script, so just skip anything that's not an array - not
really sure what else we can do here short of implementing large
parts of github actions.

* Prettier
2025-01-06 17:28:16 +00:00
Michael Telatynski
b16088d098 Update matrix-authentication-service in Playwright tests (#28876)
* Update matrix-authentication-service in Playwright tests

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

* delint

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

* Iterate

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

* Actually do the update

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

* Fix mas run

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-06 17:24:11 +00:00
David Baker
29624f7bcb Add a playwright test for backup reset / deleted (#28858)
* Add a playwright test for backup reset / deleted

A slightly tricky one to test but an important case that people can hit,
and one that otherwise wouldn't get hit a lot during normal usage, so I
think probably quite a useful test to have. Mostly though, I'm about
to change this to a toast, so I'd like a test to assert that it still works.

* Return directly

Co-authored-by: Florian Duros <florianduros@element.io>

* Fix tsdco

---------

Co-authored-by: Florian Duros <florianduros@element.io>
2025-01-06 16:32:10 +00:00
Michael Telatynski
d8f6c12c3d Fix some flaky playwright tests (#28855)
* Fix some flaky playwright tests

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

* Fix more flaky tests

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

* Iterate

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

* Reuse existing assertion

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-06 13:31:33 +00:00
David Langley
69ee8fd96a Change License: AGPL + Element Commercial (#28856)
* Add commercial licence and update config files

* Update license in headers

* Revert "Update license in headers"

This reverts commit 7ed7949485.

* Update only spdx id

* Remove LicenseRef- from package.json

LicenseRef- no longer allowed in npm v3 package.json
This fixes the warning in the logs and failing build check.
2025-01-06 11:18:54 +00:00
Richard van der Hoff
3fb10baedf Docker: run as non-root (#28849)
* Docker: allow configuration of HTTP listen port via env var

* Update docs/install.md

Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>

* prettier

* Docker: run as non-root

* Simplify sed incantation

---------

Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-03 22:54:52 +00:00
Michael Telatynski
cf49f9e22c Fix pill rendering in dev mode (#28853)
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-03 16:40:05 +00:00
Richard van der Hoff
703149d76d Docker: allow configuration of HTTP listen port via env var (#28840)
* Docker: allow configuration of HTTP listen port via env var

* Update docs/install.md

Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>

* prettier

---------

Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-03 11:46:12 +00:00
Michael Telatynski
bd3e93e8dd Fix and stabilise sliding sync playwright tests (#28809)
* Fix and stabilise sliding sync playwright tests

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

* Iterate

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

* Revert test enablement

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

* Iterate

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

* Debug

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

* Iterate

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

* Unskip now fixed tests

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-02 20:20:13 +00:00
Michael Telatynski
0555701829 Stabilise playwright tests using createRoom util (#28802)
* Stabilise playwright tests using createRoom util

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

* Pass around RoomRefs to avoid needing to cross the bridge to resolve a room to its ID

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-02 15:24:09 +00:00
Richard van der Hoff
417db4c9b2 Docker: remove redundant dos2unix calls (#28839)
I'm assuming these were here because, at some point, someone checked in some
files with CRLF line endings. However, they are no longer there, installing
dos2unix slows down the build, and just don't do that, m'kay?
2025-01-02 13:50:17 +00:00
Michael Telatynski
4e151f8d03 Update matrix-wysiwyg to consume WASM asset (#28838)
* Update matrix-wysiwyg to consume WASM asset

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

* Update matrix-wysiwyg to consume WASM asset

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

* Iterate

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-01-02 13:08:31 +00:00
ElementRobot
afa7ec695d [create-pull-request] automated change (#28777)
Co-authored-by: t3chguy <t3chguy@users.noreply.github.com>
2025-01-02 09:18:43 +00:00
ElementRobot
e98529824e [create-pull-request] automated change (#28813)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2024-12-24 06:17:08 +00:00
Michael Telatynski
16d2cccb73 OIDC settings tweaks (#28787)
* Hide 3pid account settings if account is managed externally

As they would be disabled and just confusing otherwise

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

* Show manage device button instead of sign out button for other devices in OIDC mode

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

* Tidy up

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

* Fix tests

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-23 22:35:43 +00:00
ElementRobot
9d5141cfaa [create-pull-request] automated change (#28791)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2024-12-23 20:25:18 +00:00
Michael Telatynski
1e42f28a69 Harden Settings using mapped types (#28775)
* Harden Settings using mapped types

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

* Fix issues found during hardening

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

* Iterate

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

* Iterate

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

* Iterate

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

* Remove oidc native flow stale key

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-23 20:25:15 +00:00
Michael Telatynski
4e1bd69e4d Remove stale OIDC aware docs and tests (#28805)
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-23 18:43:50 +00:00
renovate[bot]
12943954c6 Update playwright to v1.49.1 (#28803)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-23 18:25:49 +00:00
Michael Telatynski
ec95435724 Jest browser-node test (#28789)
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-23 18:06:52 +00:00
David Baker
7e1927d388 Playwright: wait for the network listener on the postgres db (#28808)
As commented. This was flaking when I was debugging it locally
(MAS was failing to start because the database wasn't ready).
2024-12-23 18:06:37 +00:00
Michael Telatynski
0b24d33c64 Simplify types (#28749) 2024-12-23 16:12:56 +00:00
Michael Telatynski
db02f26005 Delabs native OIDC support (#28615)
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-20 13:13:41 +00:00
ElementRobot
b07d10cb23 [create-pull-request] automated change (#28776)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2024-12-20 11:58:40 +00:00
Michael Telatynski
179b17434e Merge remote-tracking branch 'origin/develop' into develop 2024-12-20 11:22:31 +00:00
Michael Telatynski
ab401160f8 Fix permissions
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-20 11:22:22 +00:00
Michael Telatynski
5448de5dd6 Run Playwright tests on Firefox & "Safari" nightly (#28757)
* Run Playwright tests on Firefox

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

* Update playwright.config.ts

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Iterate

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

* Update end-to-end-tests.yaml

* Iterate

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

* Finalise

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

* Documentation

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

* typo

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-19 23:42:09 +00:00
Michael Telatynski
be181d2c79 Use mapped types around account data events (#28752)
* Use mapped types around account data events

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-19 22:53:51 +00:00
2635 changed files with 25117 additions and 16204 deletions

View File

@@ -7,3 +7,4 @@ test/end-to-end-tests/lib/
src/component-index.js
# Auto-generated file
src/modules.ts
src/modules.js

View File

@@ -1,5 +1,5 @@
module.exports = {
plugins: ["matrix-org"],
plugins: ["matrix-org", "eslint-plugin-react-compiler"],
extends: ["plugin:matrix-org/babel", "plugin:matrix-org/react", "plugin:matrix-org/a11y"],
parserOptions: {
project: ["./tsconfig.json"],
@@ -170,6 +170,8 @@ module.exports = {
"jsx-a11y/role-supports-aria-props": "off",
"matrix-org/require-copyright-header": "error",
"react-compiler/react-compiler": "error",
},
overrides: [
{
@@ -262,6 +264,7 @@ module.exports = {
// These are fine in tests
"no-restricted-globals": "off",
"react-compiler/react-compiler": "off",
},
},
{
@@ -271,6 +274,7 @@ module.exports = {
},
rules: {
"react-hooks/rules-of-hooks": ["off"],
"@typescript-eslint/no-floating-promises": ["error"],
},
},
{

21
.github/CODEOWNERS vendored
View File

@@ -3,17 +3,22 @@
/package.json @element-hq/element-web-team
/yarn.lock @element-hq/element-web-team
/src/SecurityManager.ts @element-hq/element-crypto-web-reviewers
/test/SecurityManager-test.ts @element-hq/element-crypto-web-reviewers
/src/async-components/views/dialogs/security/ @element-hq/element-crypto-web-reviewers
/src/components/views/dialogs/security/ @element-hq/element-crypto-web-reviewers
/test/components/views/dialogs/security/ @element-hq/element-crypto-web-reviewers
/src/stores/SetupEncryptionStore.ts @element-hq/element-crypto-web-reviewers
/test/stores/SetupEncryptionStore-test.ts @element-hq/element-crypto-web-reviewers
/src/SecurityManager.ts @element-hq/element-crypto-web-reviewers
/test/SecurityManager-test.ts @element-hq/element-crypto-web-reviewers
/src/async-components/views/dialogs/security/ @element-hq/element-crypto-web-reviewers
/src/components/views/dialogs/security/ @element-hq/element-crypto-web-reviewers
/test/components/views/dialogs/security/ @element-hq/element-crypto-web-reviewers
/src/stores/SetupEncryptionStore.ts @element-hq/element-crypto-web-reviewers
/test/stores/SetupEncryptionStore-test.ts @element-hq/element-crypto-web-reviewers
/src/components/views/settings/tabs/user/EncryptionUserSettingsTab.tsx @element-hq/element-crypto-web-reviewers
/src/src/components/views/settings/encryption/ @element-hq/element-crypto-web-reviewers
/test/unit-tests/components/views/settings/encryption/ @element-hq/element-crypto-web-reviewers
/playwright/e2e/settings/encryption-user-tab/ @element-hq/element-crypto-web-reviewers
/src/components/views/dialogs/devtools/Crypto.tsx @element-hq/element-crypto-web-reviewers
# Ignore translations as those will be updated by GHA for Localazy download
/src/i18n/strings
/src/i18n/strings/en_EN.json @element-hq/element-web-reviewers
# Ignore the synapse plugin as this is updated by GHA for docker image updating
/playwright/plugins/homeserver/synapse/index.ts
/playwright/testcontainers/synapse.ts

12
.github/labels.yml vendored
View File

@@ -210,6 +210,9 @@
- name: "X-Upcoming-Release-Blocker"
description: "This does not affect the current release cycle but will affect the next one"
color: "e99695"
- name: "X-Run-All-Tests"
description: "When applied to PRs, it'll run the full gamut of end-to-end tests on the PR"
color: "ff7979"
- name: "Z-Actions"
color: "ededed"
- name: "Z-Cache-Confusion"
@@ -232,6 +235,15 @@
- name: "Z-Flaky-Test"
description: "A test is raising false alarms"
color: "ededed"
- name: "Z-Flaky-Test-Chrome"
description: "Flaky playwright test in Chrome"
color: "ededed"
- name: "Z-Flaky-Test-Firefox"
description: "Flaky playwright test in Firefox"
color: "ededed"
- name: "Z-Flaky-Test-Webkit"
description: "Flaky playwright test in Webkit"
color: "ededed"
- name: "Z-Flaky-Jest-Test"
description: "A Jest test is raising false alarms"
color: "ededed"

View File

@@ -5,6 +5,9 @@ on:
branches: [develop, master]
merge_group:
types: [checks_requested]
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.sha }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
# develop pushes and repository_dispatch handled in build_develop.yaml
env:
# These must be set for fetchdep.sh to get the right branch
@@ -24,10 +27,17 @@ jobs:
- macos-14
isDevelop:
- ${{ github.event_name == 'push' && github.ref_name == 'develop' }}
isPullRequest:
- ${{ github.event_name == 'pull_request' }}
# Skip the ubuntu-24.04 build for the develop branch as the dedicated CD build_develop workflow handles that
# Skip the non-linux builds for pull requests as Windows is awfully slow, so run in merge queue only
exclude:
- isDevelop: true
image: ubuntu-24.04
- isPullRequest: true
image: windows-2022
- isPullRequest: true
image: macos-14
runs-on: ${{ matrix.image }}
defaults:
run:
@@ -37,14 +47,38 @@ jobs:
- uses: actions/setup-node@v4
with:
cache: "yarn"
# Disable cache on Windows as it is slower than not caching
# https://github.com/actions/setup-node/issues/975
cache: ${{ runner.os != 'Windows' && 'yarn' || '' }}
node-version: "lts/*"
# Workaround for yarn install timeouts, especially on Windows
- run: yarn config set network-timeout 300000
- name: Install Dependencies
run: "./scripts/layered.sh"
- name: Fetch layered build
id: layered_build
env:
# tell layered.sh to check out the right sha of the JS-SDK & EW, if they were given one
JS_SDK_GITHUB_BASE_REF: ${{ inputs.matrix-js-sdk-sha }}
run: |
scripts/layered.sh
JSSDK_SHA=$(git -C matrix-js-sdk rev-parse --short=12 HEAD)
VECTOR_SHA=$(git rev-parse --short=12 HEAD)
echo "VERSION=$VECTOR_SHA--js-$JSSDK_SHA" >> $GITHUB_OUTPUT
- name: Copy config
run: cp element.io/develop/config.json config.json
- name: Build
run: "yarn build"
env:
CI_PACKAGE: true
VERSION: "${{ steps.layered_build.outputs.VERSION }}"
run: |
yarn build
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: webapp-${{ matrix.image }}
path: webapp
retention-days: 1

View File

@@ -26,6 +26,12 @@ jobs:
R2_URL: ${{ vars.CF_R2_S3_API }}
R2_PUBLIC_URL: "https://element-web-develop.element.io"
steps:
# Workaround for https://www.cloudflarestatus.com/incidents/t5nrjmpxc1cj
- uses: unfor19/install-aws-cli-action@v1
with:
version: 2.22.35
verbose: false
arch: amd64
- uses: actions/checkout@v4
- uses: actions/setup-node@v4

View File

@@ -96,3 +96,4 @@ jobs:
projectName: ${{ env.SITE == 'staging.element.io' && 'element-web-staging' || 'element-web' }}
directory: _deploy
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
branch: main

View File

@@ -24,10 +24,10 @@ jobs:
uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3
- name: Set up QEMU
uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3
uses: docker/setup-qemu-action@53851d14592bedcffcf25ea515637cff71ef929a # v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # v3
uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3
with:
install: true
@@ -51,7 +51,7 @@ jobs:
- name: Build and push
id: build-and-push
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6
uses: docker/build-push-action@67a2d409c0a876cbe6b11854e3e25193efe4e62d # v6
with:
context: .
push: true

View File

@@ -3,6 +3,9 @@
# as an artifact and run end-to-end tests.
name: End to End Tests
on:
# CRON to run all Projects at 6am UTC
schedule:
- cron: "0 6 * * *"
pull_request: {}
merge_group:
types: [checks_requested]
@@ -32,6 +35,8 @@ concurrency:
env:
# fetchdep.sh needs to know our PR number
PR_NUMBER: ${{ github.event.pull_request.number }}
# Use 6 runners in the default case, but 4 when running on a schedule where we run all 5 projects (20 runners total)
NUM_RUNNERS: ${{ github.event_name == 'schedule' && 4 || 6 }}
permissions: {} # No permissions required
@@ -40,6 +45,9 @@ jobs:
name: "Build Element-Web"
runs-on: ubuntu-24.04
if: inputs.skip != true
outputs:
num-runners: ${{ env.NUM_RUNNERS }}
runners-matrix: ${{ steps.runner-vars.outputs.matrix }}
steps:
- name: Checkout code
uses: actions/checkout@v4
@@ -79,8 +87,17 @@ jobs:
path: webapp
retention-days: 1
- name: Calculate runner variables
id: runner-vars
uses: actions/github-script@v7
with:
script: |
const numRunners = parseInt(process.env.NUM_RUNNERS, 10);
const matrix = Array.from({ length: numRunners }, (_, i) => i + 1);
core.setOutput("matrix", JSON.stringify(matrix));
playwright:
name: "Run Tests ${{ matrix.runner }}/${{ strategy.job-total }}"
name: "Run Tests [${{ matrix.project }}] ${{ matrix.runner }}/${{ needs.build.outputs.num-runners }}"
needs: build
if: inputs.skip != true
runs-on: ubuntu-24.04
@@ -92,7 +109,25 @@ jobs:
fail-fast: false
matrix:
# Run multiple instances in parallel to speed up the tests
runner: [1, 2, 3, 4, 5, 6]
runner: ${{ fromJSON(needs.build.outputs.runners-matrix) }}
project:
- Chrome
- Firefox
- WebKit
- Dendrite
- Pinecone
runAllTests:
- ${{ github.event_name == 'schedule' || contains(github.event.pull_request.labels.*.name, 'X-Run-All-Tests') }}
# Skip the Firefox & Safari runs unless this was a cron trigger or PR has X-Run-All-Tests label
exclude:
- runAllTests: false
project: Firefox
- runAllTests: false
project: WebKit
- runAllTests: false
project: Dendrite
- runAllTests: false
project: Pinecone
steps:
- uses: actions/checkout@v4
with:
@@ -124,24 +159,30 @@ jobs:
with:
path: |
~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ steps.playwright.outputs.version }}-chromium
key: ${{ runner.os }}-playwright-${{ steps.playwright.outputs.version }}
- name: Install Playwright browser
- name: Install Playwright browsers
if: steps.playwright-cache.outputs.cache-hit != 'true'
run: yarn playwright install --with-deps --no-shell chromium
run: yarn playwright install --with-deps --no-shell
- name: Install system dependencies for WebKit
# Some WebKit dependencies seem to lay outside the cache and will need to be installed separately
if: matrix.project == 'WebKit' && steps.playwright-cache.outputs.cache-hit == 'true'
run: yarn playwright install-deps webkit
# We skip tests tagged with @mergequeue when running on PRs, but run them in MQ and everywhere else
- name: Run Playwright tests
run: |
yarn playwright test \
--shard "${{ matrix.runner }}/${{ strategy.job-total }}" \
${{ github.event_name == 'pull_request' && '--grep-invert @mergequeue' || '' }}
--shard "${{ matrix.runner }}/${{ needs.build.outputs.num-runners }}" \
--project="${{ matrix.project }}" \
${{ (github.event_name == 'pull_request' && matrix.runAllTests == false ) && '--grep-invert @mergequeue' || '' }}
- name: Upload blob report to GitHub Actions Artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: all-blob-reports-${{ matrix.runner }}
name: all-blob-reports-${{ matrix.project }}-${{ matrix.runner }}
path: blob-report
retention-days: 1

View File

@@ -3,7 +3,8 @@ on:
workflow_dispatch: {}
schedule:
- cron: "0 6 * * 1,3,5" # Every Monday, Wednesday and Friday at 6am UTC
permissions: {} # We use ELEMENT_BOT_TOKEN instead
permissions:
pull-requests: write # needed to auto-approve PRs
jobs:
download:
uses: matrix-org/matrix-web-i18n/.github/workflows/localazy_download.yaml@main

View File

@@ -3,7 +3,7 @@
name: Upload Preview Build to Netlify
on:
workflow_run:
workflows: ["End to End Tests"]
workflows: ["Build"]
types:
- completed
jobs:
@@ -32,7 +32,7 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}
name: webapp
name: webapp-ubuntu-24.04
path: webapp
- name: 📤 Deploy to Netlify

View File

@@ -17,13 +17,13 @@ jobs:
docker pull "$IMAGE"
INSPECT=$(docker inspect --format='{{index .RepoDigests 0}}' "$IMAGE")
DIGEST=${INSPECT#*@}
sed -i "s/const DOCKER_TAG.*/const DOCKER_TAG = \"develop@$DIGEST\";/" playwright/plugins/homeserver/synapse/index.ts
sed -i "s/const TAG.*/const TAG = \"develop@$DIGEST\";/" playwright/testcontainers/synapse.ts
env:
IMAGE: ghcr.io/element-hq/synapse:develop
- name: Create Pull Request
id: cpr
uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7
with:
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
branch: actions/playwright-image-updates

View File

@@ -132,9 +132,3 @@ jobs:
- name: Run linter
run: "yarn run lint:knip"
- name: Install Deps
run: "scripts/layered.sh"
- name: Dead Code Analysis
run: "yarn run analyse:unused-exports"

View File

@@ -104,7 +104,7 @@ jobs:
- name: Skip SonarCloud in merge queue
if: github.event_name == 'merge_group' || inputs.disable_coverage == 'true'
uses: guibranco/github-status-action-v2@d469d49426f5a7b8a1fbcac20ad274d3e4892321
uses: guibranco/github-status-action-v2@ecd54a02cf761e85a8fb328fe937710fd4227cda
with:
authToken: ${{ secrets.GITHUB_TOKEN }}
state: success

View File

@@ -1,5 +1,6 @@
name: Close stale flaky issues
on:
workflow_dispatch: {}
schedule:
- cron: "30 1 * * *"
permissions: {}
@@ -17,3 +18,4 @@ jobs:
days-before-close: 0
close-issue-message: "This flaky test issue has not been updated in 14 days. It is being closed as presumed resolved."
exempt-issue-labels: "Z-Flaky-Test-Disabled"
operations-per-run: 100

View File

@@ -23,7 +23,7 @@ jobs:
run: "yarn update:jitsi"
- name: Create Pull Request
uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7
with:
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
branch: actions/jitsi-update

1
.gitignore vendored
View File

@@ -26,6 +26,7 @@ electron/pub
/coverage
# Auto-generated file
/src/modules.ts
/src/modules.js
/build_config.yaml
/book
/index.html

View File

@@ -17,6 +17,7 @@ electron/pub
/coverage
# Auto-generated file
/src/modules.ts
/src/modules.js
/src/i18n/strings
/build_config.yaml
# Raises an error because it contains a template var breaking the script tag

View File

@@ -1,3 +1,39 @@
Changes in [1.11.91](https://github.com/element-hq/element-web/releases/tag/v1.11.91) (2025-01-28)
==================================================================================================
## ✨ Features
* Implement changes to memberlist from feedback ([#29029](https://github.com/element-hq/element-web/pull/29029)). Contributed by @MidhunSureshR.
* Add toast for recovery keys being out of sync ([#28946](https://github.com/element-hq/element-web/pull/28946)). Contributed by @dbkr.
* Refactor LegacyCallHandler event emitter to use TypedEventEmitter ([#29008](https://github.com/element-hq/element-web/pull/29008)). Contributed by @t3chguy.
* Add `Recovery` section in the new user settings `Encryption` tab ([#28673](https://github.com/element-hq/element-web/pull/28673)). Contributed by @florianduros.
* Retry loading chunks to make the app more resilient ([#29001](https://github.com/element-hq/element-web/pull/29001)). Contributed by @t3chguy.
* Clear account idb table on logout ([#28996](https://github.com/element-hq/element-web/pull/28996)). Contributed by @t3chguy.
* Implement new memberlist design with MVVM architecture ([#28874](https://github.com/element-hq/element-web/pull/28874)). Contributed by @MidhunSureshR.
## 🐛 Bug Fixes
* [Backport staging] Switch to secure random strings ([#29035](https://github.com/element-hq/element-web/pull/29035)). Contributed by @RiotRobot.
* React to MatrixEvent sender/target being updated for rendering state events ([#28947](https://github.com/element-hq/element-web/pull/28947)). Contributed by @t3chguy.
Changes in [1.11.90](https://github.com/element-hq/element-web/releases/tag/v1.11.90) (2025-01-14)
==================================================================================================
## ✨ Features
* Docker: run as non-root ([#28849](https://github.com/element-hq/element-web/pull/28849)). Contributed by @richvdh.
* Docker: allow configuration of HTTP listen port via env var ([#28840](https://github.com/element-hq/element-web/pull/28840)). Contributed by @richvdh.
* Update matrix-wysiwyg to consume WASM asset ([#28838](https://github.com/element-hq/element-web/pull/28838)). Contributed by @t3chguy.
* OIDC settings tweaks ([#28787](https://github.com/element-hq/element-web/pull/28787)). Contributed by @t3chguy.
* Delabs native OIDC support ([#28615](https://github.com/element-hq/element-web/pull/28615)). Contributed by @t3chguy.
* Move room header info button to right-most position ([#28754](https://github.com/element-hq/element-web/pull/28754)). Contributed by @t3chguy.
* Enable key backup by default ([#28691](https://github.com/element-hq/element-web/pull/28691)). Contributed by @dbkr.
## 🐛 Bug Fixes
* Fix building the automations mermaid diagram ([#28881](https://github.com/element-hq/element-web/pull/28881)). Contributed by @dbkr.
* Playwright: wait for the network listener on the postgres db ([#28808](https://github.com/element-hq/element-web/pull/28808)). Contributed by @dbkr.
Changes in [1.11.89](https://github.com/element-hq/element-web/releases/tag/v1.11.89) (2024-12-18)
==================================================================================================
This is a patch release to fix a bug which could prevent loading stored crypto state from storage, and also to fix URL previews when switching back to a room.

View File

@@ -1,20 +1,17 @@
# Builder
FROM --platform=$BUILDPLATFORM node:22-bullseye as builder
FROM --platform=$BUILDPLATFORM node:22-bullseye AS builder
# Support custom branch of the js-sdk. This also helps us build images of element-web develop.
ARG USE_CUSTOM_SDKS=false
ARG JS_SDK_REPO="https://github.com/matrix-org/matrix-js-sdk.git"
ARG JS_SDK_BRANCH="master"
RUN apt-get update && apt-get install -y git dos2unix
WORKDIR /src
COPY . /src
RUN dos2unix /src/scripts/docker-link-repos.sh && bash /src/scripts/docker-link-repos.sh
RUN /src/scripts/docker-link-repos.sh
RUN yarn --network-timeout=200000 install
RUN dos2unix /src/scripts/docker-package.sh /src/scripts/get-version-from-git.sh /src/scripts/normalize-version.sh && bash /src/scripts/docker-package.sh
RUN /src/scripts/docker-package.sh
# Copy the config now so that we don't create another layer in the app image
RUN cp /src/config.sample.json /src/webapp/config.json
@@ -24,8 +21,22 @@ FROM nginx:alpine-slim
COPY --from=builder /src/webapp /app
# Override default nginx config
COPY /nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf
# Override default nginx config. Templates in `/etc/nginx/templates` are passed
# through `envsubst` by the nginx docker image entry point.
COPY /docker/nginx-templates/* /etc/nginx/templates/
# Tell nginx to put its pidfile elsewhere, so it can run as non-root
RUN sed -i -e 's,/var/run/nginx.pid,/tmp/nginx.pid,' /etc/nginx/nginx.conf
# nginx user must own the cache and etc directory to write cache and tweak the nginx config
RUN chown -R nginx:0 /var/cache/nginx /etc/nginx
RUN chmod -R g+w /var/cache/nginx /etc/nginx
RUN rm -rf /usr/share/nginx/html \
&& ln -s /app /usr/share/nginx/html
# Run as nginx user by default
USER nginx
# HTTP listen port
ENV ELEMENT_WEB_PORT=80

6
LICENSE-COMMERCIAL Normal file
View File

@@ -0,0 +1,6 @@
Licensees holding a valid commercial license with Element may use this
software in accordance with the terms contained in a written agreement
between you and Element.
To purchase a commercial license please contact our sales team at
licensing@element.io

View File

@@ -311,3 +311,18 @@ For a developer guide, see the [translating dev doc](docs/translating-dev.md).
Issues are triaged by community members and the Web App Team, following the [triage process](https://github.com/element-hq/element-meta/wiki/Triage-process).
We use [issue labels](https://github.com/element-hq/element-meta/wiki/Issue-labelling) to sort all incoming issues.
## Copyright & License
Copyright (c) 2014-2017 OpenMarket Ltd
Copyright (c) 2017 Vector Creations Ltd
Copyright (c) 2017-2025 New Vector Ltd
This software is multi licensed by New Vector Ltd (Element). It can be used either:
(1) for free under the terms of the GNU Affero General Public License (as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version); OR
(2) for free under the terms of the GNU General Public License (as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version); OR
(3) under the terms of a paid-for Element Commercial License agreement between you and Element (the terms of which may vary depending on what you and Element have agreed to).
Unless required by applicable law or agreed to in writing, software distributed under the Licenses is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the Licenses for the specific language governing permissions and limitations under the Licenses.

View File

@@ -2,7 +2,7 @@
Copyright 2024 New Vector Ltd.
Copyright 2022 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
@@ -17,6 +17,7 @@ class MockMap extends EventEmitter {
setCenter = jest.fn();
setStyle = jest.fn();
fitBounds = jest.fn();
remove = jest.fn();
}
const MockMapInstance = new MockMap();

View File

@@ -2,7 +2,7 @@
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/

View File

@@ -3,7 +3,7 @@
"description": "A glossy Matrix collaboration client for the web.",
"repository": {
"url": "https://github.com/element-hq/element-web",
"license": "AGPL-3.0-only OR GPL-3.0-only"
"license": "AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial"
},
"bugs": {
"list": "https://github.com/element-hq/element-web/issues",

View File

@@ -1,6 +1,6 @@
server {
listen 80;
listen [::]:80;
listen ${ELEMENT_WEB_PORT};
listen [::]:${ELEMENT_WEB_PORT};
server_name localhost;
root /usr/share/nginx/html;

View File

@@ -60,6 +60,22 @@ would be:
docker run --rm -p 127.0.0.1:80:80 -v /etc/element-web/config.json:/app/config.json vectorim/element-web
```
The Docker image is configured to run as an unprivileged (non-root) user by
default. This should be fine on modern Docker runtimes, but binding to port 80
on other runtimes may require root privileges. To resolve this, either run the
image as root (`docker run --user 0`) or, better, change the port that nginx
listens on via the `ELEMENT_WEB_PORT` environment variable.
The behaviour of the docker image can be customised via the following
environment variables:
- `ELEMENT_WEB_PORT`
The port to listen on (within the docker container) for HTTP
traffic. Defaults to `80`.
### Building the docker image
To build the image yourself:
```bash

View File

@@ -1,29 +1,9 @@
# OIDC and delegated authentication
## Compatibility/OIDC-aware mode
[MSC2965: OIDC provider discovery](https://github.com/matrix-org/matrix-spec-proposals/pull/2965)
[MSC3824: OIDC aware clients](https://github.com/matrix-org/matrix-spec-proposals/pull/3824)
This mode uses an SSO flow to gain a `loginToken` from the authentication provider, then continues with SSO login.
Element Web uses [MSC2965: OIDC provider discovery](https://github.com/matrix-org/matrix-spec-proposals/pull/2965) to discover the configured provider.
Wherever valid MSC2965 configuration is discovered, OIDC-aware login flow will be the only option offered.
## (🧪Experimental) OIDC-native flow
Can be enabled by a config-level-only setting in `config.json`
```json
{
"features": {
"feature_oidc_native_flow": true
}
}
```
See https://areweoidcyet.com/client-implementation-guide/ for implementation details.
Element Web uses [MSC2965: OIDC provider discovery](https://github.com/matrix-org/matrix-spec-proposals/pull/2965) to discover the configured provider.
Where OIDC native login flow is enabled and valid MSC2965 configuration is discovered, OIDC native login flow will be the only login option offered.
Where a valid MSC2965 configuration is discovered, OIDC native login flow will be the only login option offered.
Element Web will attempt to [dynamically register](https://openid.net/specs/openid-connect-registration-1_0.html) with the configured OP.
Then, authentication will be completed [as described here](https://areweoidcyet.com/client-implementation-guide/).

View File

@@ -23,21 +23,19 @@ element-web project is fine: leave it running it a different terminal as you wou
when developing. Alternatively if you followed the development set up from element-web then
Playwright will be capable of running the webserver on its own if it isn't already running.
The tests use Docker to launch Homeserver (Synapse or Dendrite) instances to test against, so you'll also
need to have Docker installed and working in order to run the Playwright tests.
The tests use [testcontainers](https://node.testcontainers.org/) to launch Homeserver (Synapse or Dendrite)
instances to test against, so you'll also need to one of the
[supported container runtimes](#supporter-container-runtimes)
installed and working in order to run the Playwright tests.
There are a few different ways to run the tests yourself. The simplest is to run:
```shell
docker pull ghcr.io/element-hq/synapse:develop
yarn run test:playwright
```
This will run the Playwright 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:
```shell
@@ -53,41 +51,36 @@ yarn run test:playwright:open --headed --debug
See more command line options at <https://playwright.dev/docs/test-cli>.
### Running with Rust cryptography
## Projects
`matrix-js-sdk` is currently in the
[process](https://github.com/vector-im/element-web/issues/21972) of being
updated to replace its end-to-end encryption implementation to use the [Matrix
Rust SDK](https://github.com/matrix-org/matrix-rust-sdk). This is not currently
enabled by default, but it is possible to have Playwright configure Element to use
the Rust crypto implementation by passing `--project="Rust Crypto"` or using
the top left options in open mode.
By default, Playwright will run all "Projects", this means tests will run against Chrome, Firefox and "Safari" (Webkit).
We only run tests against Chrome in pull request CI, but all projects in the merge queue.
Some tests are excluded from running on certain browsers due to incompatibilities in the test harness.
## How the Tests Work
Everything Playwright-related lives in the `playwright/` subdirectory of react-sdk
Everything Playwright-related lives in the `playwright/` subdirectory
as is typical for Playwright tests. Likewise, tests live in `playwright/e2e`.
`playwright/plugins/homeservers` contains Playwright plugins that starts instances
of Synapse/Dendrite in Docker containers. These servers are what Element-web runs
against in the tests.
`playwright/testcontainers` contains the testcontainers which start instances
of Synapse/Dendrite. These servers are what Element-web runs against in the tests.
Synapse can be launched with different configurations in order to test element
in different configurations. `playwright/plugins/homeserver/synapse/templates`
contains template configuration files for each different configuration.
in different configurations. You can specify `synapseConfig` as such:
Each test suite can then launch whatever Synapse instances it needs in whatever
configurations.
```typescript
test.use({
synapseConfig: {
// The config options to pass to the Synapse instance
},
});
```
Note that although tests should stop the Homeserver instances after running and the
plugin also stop any remaining instances after all tests have run, it is possible
to be left with some stray containers if, for example, you terminate a test such
that the `after()` does not run and also exit Playwright uncleanly. All the containers
it starts are prefixed, so they are easy to recognise. They can be removed safely.
After each test run, logs from the Synapse instances are saved in `playwright/logs/synapse`
with each instance in a separate directory named after its ID. These logs are removed
at the start of each test run.
The appropriate homeserver will be launched by the Playwright worker and reused for all tests which match the worker configuration.
Due to homeservers being reused between tests, please use unique names for any rooms put into the room directory as
they may be visible from other tests, the suggested approach is to use `testInfo.testId` within the name or lodash's uniqueId.
We remove public rooms from the room directory between tests but deleting users doesn't have a homeserver agnostic solution.
The logs from testcontainers will be attached to any reports output from Playwright.
## Writing Tests
@@ -117,25 +110,6 @@ Homeserver instances should be reasonably cheap to start (you may see the first
while as it pulls the Docker image).
You do not need to explicitly clean up the instance as it will be cleaned up by the fixture.
### Synapse Config Templates
When a Synapse instance is started, it's given a config generated from one of the config
templates in `playwright/plugins/homeserver/synapse/templates`. There are a couple of special files
in these templates:
- `homeserver.yaml`:
Template substitution happens in this file. Template variables are:
- `REGISTRATION_SECRET`: The secret used to register users via the REST API.
- `MACAROON_SECRET_KEY`: Generated each time for security
- `FORM_SECRET`: Generated each time for security
- `PUBLIC_BASEURL`: The localhost url + port combination the synapse is accessible at
- `localhost.signing.key`: A signing key is auto-generated and saved to this file.
Config templates should not contain a signing key and instead assume that one will exist
in this file.
All other files in the template are copied recursively to `/data/`, so the file `foo.html`
in a template can be referenced in the config as `/data/foo.html`.
### Logging In
We again heavily leverage the magic of [Playwright fixtures](https://playwright.dev/docs/test-fixtures).
@@ -224,3 +198,20 @@ We use test tags to categorise tests for running subsets more efficiently.
- `@mergequeue`: Tests that are slow or flaky and cover areas of the app we update seldom, should not be run on every PR commit but will be run in the Merge Queue.
- `@screenshot`: Tests that use `toMatchScreenshot` to speed up a run of `test:playwright:screenshots`. A test with this tag must not also have the `@mergequeue` tag as this would cause false positives in the stale screenshot detection.
- `@no-$project`: Tests which are unsupported in $Project. These tests will be skipped when running in $Project.
Anything testing Matrix media will need to have `@no-firefox` and `@no-webkit` as those rely on the service worker which
has to be disabled in Playwright on Firefox & Webkit to retain routing functionality.
Anything testing VoIP/microphone will need to have `@no-webkit` as fake microphone functionality is not available
there at this time.
If you wish to run all tests in a PR, you can give it the label `X-Run-All-Tests`.
## Supporter container runtimes
We use testcontainers to spin up various instances of Synapse, Matrix Authentication Service, and more.
It supports Docker out of the box but also has support for Podman, Colima, Rancher, you just need to follow some instructions to achieve it:
https://node.testcontainers.org/supported-container-runtimes/
If you are running under Colima, you may need to set the environment variable `TMPDIR` to `/tmp/colima` or a path
within `$HOME` to allow bind mounting temporary directories into the Docker containers.

View File

@@ -8,11 +8,13 @@
#### develop
The develop branch holds the very latest and greatest code we have to offer, as such it may be less stable. It corresponds to the develop.element.io CD platform.
The develop branch holds the very latest and greatest code we have to offer, as such it may be less stable.
It is auto-deployed on every commit to element-web or matrix-js-sdk to develop.element.io via GitHub Actions `build_develop.yml`.
#### staging
The staging branch corresponds to the very latest release regardless of whether it is an RC or not. Deployed to staging.element.io manually.
It is auto-deployed on every release of element-web to staging.element.io via GitHub Actions `deploy.yml`.
#### master
@@ -215,7 +217,7 @@ We ship Element Web to dockerhub, `*.element.io`, and packages.element.io.
We ship Element Desktop to packages.element.io.
- [ ] Check that element-web has shipped to dockerhub
- [ ] Deploy staging.element.io. [See docs.](https://handbook.element.io/books/element-web-team/page/deploying-appstagingelementio)
- [ ] Check that the staging [deployment](https://github.com/element-hq/element-web/actions/workflows/deploy.yml) has completed successfully
- [ ] Test staging.element.io
For final releases additionally do these steps:
@@ -225,6 +227,9 @@ For final releases additionally do these steps:
- [ ] Ensure Element Web package has shipped to packages.element.io
- [ ] Ensure Element Desktop packages have shipped to packages.element.io
If you need to roll back a deployment to staging.element.io,
you can run the `deploy.yml` automation choosing an older tag which you wish to deploy.
# Housekeeping
We have some manual housekeeping to do in order to prepare for the next release.

View File

@@ -2,7 +2,7 @@
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
@@ -14,6 +14,8 @@ const config: Config = {
testEnvironment: "jsdom",
testEnvironmentOptions: {
url: "http://localhost/",
// This is needed to be able to load dual CJS/ESM WASM packages e.g. rust crypto & matrix-wywiwyg
customExportConditions: ["browser", "node"],
},
testMatch: ["<rootDir>/test/**/*-test.[tj]s?(x)"],
globalSetup: "<rootDir>/test/globalSetup.ts",

13
knip.ts
View File

@@ -10,13 +10,13 @@ export default {
"playwright/**",
"test/**",
"res/decoder-ring/**",
"res/jitsi_external_api.min.js",
"docs/**",
// Used by jest
"__mocks__/maplibre-gl.js",
],
project: ["**/*.{js,ts,jsx,tsx}"],
ignore: [
"docs/**",
"res/jitsi_external_api.min.js",
// Used by jest
"__mocks__/maplibre-gl.js",
// Keep for now
"src/hooks/useLocalStorageState.ts",
"src/components/views/elements/InfoTooltip.tsx",
@@ -37,13 +37,8 @@ export default {
// False positive
"sw.js",
// Used by webpack
"buffer",
"process",
"util",
// Used by workflows
"ts-prune",
// Required due to bug in bloom-filters https://github.com/Callidon/bloom-filters/issues/75
"@types/seedrandom",
],
ignoreBinaries: [
// Used in scripts & workflows

View File

@@ -1,7 +1,7 @@
/*
Copyright 2022-2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/

View File

@@ -1,7 +1,7 @@
/*
Copyright 2022-2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
@@ -23,10 +23,9 @@ const MODULES_TS_HEADER = `
* You are not a salmon.
*/
import { RuntimeModule } from "@matrix-org/react-sdk-module-api/lib/RuntimeModule";
`;
const MODULES_TS_DEFINITIONS = `
export const INSTALLED_MODULES: RuntimeModule[] = [];
export const INSTALLED_MODULES = [];
`;
export function installer(config: BuildConfig): void {
@@ -78,8 +77,8 @@ export function installer(config: BuildConfig): void {
return; // hit the finally{} block before exiting
}
// If we reach here, everything seems fine. Write modules.ts and log some output
// Note: we compile modules.ts in two parts for developer friendliness if they
// If we reach here, everything seems fine. Write modules.js and log some output
// Note: we compile modules.js in two parts for developer friendliness if they
// happen to look at it.
console.log("The following modules have been installed: ", installedModules);
let modulesTsHeader = MODULES_TS_HEADER;
@@ -193,5 +192,5 @@ function isModuleVersionCompatible(ourApiVersion: string, moduleApiVersion: stri
}
function writeModulesTs(content: string): void {
fs.writeFileSync("./src/modules.ts", content, "utf-8");
fs.writeFileSync("./src/modules.js", content, "utf-8");
}

View File

@@ -1,7 +1,7 @@
/*
Copyright 2022-2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/

View File

@@ -1,13 +1,13 @@
{
"name": "element-web",
"version": "1.11.89",
"version": "1.11.91",
"description": "A feature-rich client for Matrix.org",
"author": "New Vector Ltd.",
"repository": {
"type": "git",
"url": "https://github.com/element-hq/element-web"
},
"license": "AGPL-3.0-only OR GPL-3.0-only",
"license": "SEE LICENSE IN README.md",
"files": [
"lib",
"res",
@@ -64,16 +64,17 @@
"test:playwright:open": "yarn test:playwright --ui",
"test:playwright:screenshots": "yarn test:playwright:screenshots:build && yarn test:playwright:screenshots:run",
"test:playwright:screenshots:build": "docker build playwright -t element-web-playwright",
"test:playwright:screenshots:run": "docker run --rm --network host -e BASE_URL -e CI -v $(pwd):/work/ -v $(node -e 'console.log(require(`path`).dirname(require.resolve(`matrix-js-sdk/package.json`)))'):/work/node_modules/matrix-js-sdk -v /var/run/docker.sock:/var/run/docker.sock -v /tmp/:/tmp/ -it element-web-playwright --grep @screenshot",
"test:playwright:screenshots:run": "docker run --rm --network host -e BASE_URL -e CI -v $(pwd):/work/ -v $(node -e 'console.log(require(`path`).dirname(require.resolve(`matrix-js-sdk/package.json`)))'):/work/node_modules/matrix-js-sdk -v /var/run/docker.sock:/var/run/docker.sock -v /tmp/:/tmp/ -it element-web-playwright --grep @screenshot --project=Chrome",
"coverage": "yarn test --coverage",
"analyse:unused-exports": "ts-node ./scripts/analyse_unused_exports.ts",
"analyse:webpack-bundles": "webpack-bundle-analyzer webpack-stats.json webapp",
"update:jitsi": "curl -s https://meet.element.io/libs/external_api.min.js > ./res/jitsi_external_api.min.js"
},
"resolutions": {
"@types/react": "18.3.18",
"@types/react-dom": "18.3.5",
"oidc-client-ts": "3.1.0",
"jwt-decode": "4.0.0",
"caniuse-lite": "1.0.30001684",
"caniuse-lite": "1.0.30001692",
"wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0",
"wrap-ansi": "npm:wrap-ansi@^7.0.0"
},
@@ -83,13 +84,15 @@
"@fontsource/inter": "^5",
"@formatjs/intl-segmenter": "^11.5.7",
"@matrix-org/analytics-events": "^0.29.0",
"@matrix-org/emojibase-bindings": "^1.3.3",
"@matrix-org/emojibase-bindings": "^1.3.4",
"@matrix-org/react-sdk-module-api": "^2.4.0",
"@matrix-org/spec": "^1.7.0",
"@sentry/browser": "^8.0.0",
"@vector-im/compound-design-tokens": "^2.0.1",
"@vector-im/compound-web": "^7.5.0",
"@vector-im/matrix-wysiwyg": "2.37.13",
"@types/png-chunks-extract": "^1.0.2",
"@types/react-virtualized": "^9.21.30",
"@vector-im/compound-design-tokens": "^2.1.0",
"@vector-im/compound-web": "^7.6.1",
"@vector-im/matrix-wysiwyg": "2.38.0",
"@zxcvbn-ts/core": "^3.0.4",
"@zxcvbn-ts/language-common": "^3.0.4",
"@zxcvbn-ts/language-en": "^3.0.2",
@@ -121,7 +124,7 @@
"linkify-string": "4.2.0",
"linkifyjs": "4.2.0",
"lodash": "^4.17.21",
"maplibre-gl": "^4.0.0",
"maplibre-gl": "^5.0.0",
"matrix-encrypt-attachment": "^1.0.3",
"matrix-events-sdk": "0.0.1",
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
@@ -134,16 +137,17 @@
"png-chunks-extract": "^1.0.0",
"posthog-js": "1.157.2",
"qrcode": "1.5.4",
"re-resizable": "6.10.1",
"re-resizable": "6.10.3",
"react": "^18.3.1",
"react-beautiful-dnd": "^13.1.0",
"react-blurhash": "^0.3.0",
"react-dom": "^18.3.1",
"react-focus-lock": "^2.5.1",
"react-transition-group": "^4.4.1",
"react-virtualized": "^9.22.5",
"rfc4648": "^1.4.0",
"sanitize-filename": "^1.6.3",
"sanitize-html": "2.13.1",
"sanitize-html": "2.14.0",
"tar-js": "^0.3.0",
"temporal-polyfill": "^0.2.5",
"ua-parser-js": "^1.0.2",
@@ -174,9 +178,10 @@
"@peculiar/webcrypto": "^1.4.3",
"@playwright/test": "^1.40.1",
"@principalstudio/html-webpack-inject-preload": "^1.2.7",
"@sentry/webpack-plugin": "^2.7.1",
"@sentry/webpack-plugin": "^3.0.0",
"@stylistic/eslint-plugin": "^2.9.0",
"@svgr/webpack": "^8.0.0",
"@testcontainers/postgresql": "^10.16.0",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.4.8",
"@testing-library/react": "^16.0.0",
@@ -188,7 +193,6 @@
"@types/escape-html": "^1.0.1",
"@types/express": "^5.0.0",
"@types/file-saver": "^2.0.3",
"@types/fs-extra": "^11.0.0",
"@types/glob-to-regexp": "^0.4.1",
"@types/jest": "29.5.12",
"@types/jitsi-meet": "^2.0.2",
@@ -201,17 +205,17 @@
"@types/node-fetch": "^2.6.2",
"@types/pako": "^2.0.0",
"@types/qrcode": "^1.3.5",
"@types/react": "18.3.3",
"@types/react": "18.3.18",
"@types/react-beautiful-dnd": "^13.0.0",
"@types/react-dom": "18.3.1",
"@types/react-dom": "18.3.5",
"@types/react-transition-group": "^4.4.0",
"@types/sanitize-html": "2.13.0",
"@types/semver": "^7.5.8",
"@types/tar-js": "^0.3.5",
"@types/ua-parser-js": "^0.7.36",
"@types/uuid": "^10.0.0",
"@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/parser": "^8.0.0",
"@typescript-eslint/eslint-plugin": "^8.19.0",
"@typescript-eslint/parser": "^8.19.0",
"babel-jest": "^29.0.0",
"babel-loader": "^9.0.0",
"babel-plugin-jsx-remove-data-test-id": "^3.0.0",
@@ -226,13 +230,14 @@
"dotenv": "^16.0.2",
"eslint": "8.57.1",
"eslint-config-google": "^0.14.0",
"eslint-config-prettier": "^9.0.0",
"eslint-config-prettier": "^10.0.0",
"eslint-plugin-deprecate": "0.8.5",
"eslint-plugin-import": "^2.25.4",
"eslint-plugin-jest": "^28.0.0",
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-matrix-org": "^2.0.2",
"eslint-plugin-react": "^7.28.0",
"eslint-plugin-react-compiler": "^19.0.0-beta-df7b47d-20241124",
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-unicorn": "^56.0.0",
"express": "^4.18.2",
@@ -240,7 +245,6 @@
"fetch-mock": "9.11.0",
"fetch-mock-jest": "^1.5.1",
"file-loader": "^6.0.0",
"fs-extra": "^11.0.0",
"glob": "^11.0.0",
"html-webpack-plugin": "^5.5.3",
"husky": "^9.0.0",
@@ -252,14 +256,14 @@
"jsqr": "^1.4.0",
"knip": "^5.36.2",
"lint-staged": "^15.0.2",
"mailhog": "^4.16.0",
"mailpit-api": "^1.0.5",
"matrix-web-i18n": "^3.2.1",
"mini-css-extract-plugin": "2.9.0",
"mini-css-extract-plugin": "2.9.2",
"minimist": "^1.2.6",
"modernizr": "^3.12.0",
"node-fetch": "^2.6.7",
"playwright-core": "^1.45.1",
"postcss": "8.4.38",
"postcss": "8.4.46",
"postcss-easings": "^4.0.0",
"postcss-hexrgba": "2.1.0",
"postcss-import": "16.1.0",
@@ -275,20 +279,22 @@
"rimraf": "^6.0.0",
"semver": "^7.5.2",
"source-map-loader": "^5.0.0",
"strip-ansi": "^7.1.0",
"stylelint": "^16.1.0",
"stylelint-config-standard": "^36.0.0",
"stylelint-scss": "^6.0.0",
"stylelint-value-no-unknown-custom-properties": "^6.0.1",
"terser-webpack-plugin": "^5.3.9",
"testcontainers": "^10.16.0",
"ts-node": "^10.9.1",
"ts-prune": "^0.10.3",
"typescript": "5.7.2",
"typescript": "5.7.3",
"util": "^0.12.5",
"web-streams-polyfill": "^4.0.0",
"webpack": "^5.89.0",
"webpack-bundle-analyzer": "^4.8.0",
"webpack-cli": "^5.0.0",
"webpack-cli": "^6.0.0",
"webpack-dev-server": "^5.0.0",
"webpack-retry-chunk-load-plugin": "^3.1.1",
"webpack-version-file-plugin": "^0.5.0",
"yaml": "^2.3.3"
},

View File

@@ -2,25 +2,80 @@
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { defineConfig, devices } from "@playwright/test";
import { Options } from "./playwright/services";
const baseURL = process.env["BASE_URL"] ?? "http://localhost:8080";
export default defineConfig({
projects: [{ name: "Chrome", use: { ...devices["Desktop Chrome"], channel: "chromium" } }],
const chromeProject = {
...devices["Desktop Chrome"],
channel: "chromium",
permissions: ["clipboard-write", "clipboard-read", "microphone"],
launchOptions: {
args: ["--use-fake-ui-for-media-stream", "--use-fake-device-for-media-stream", "--mute-audio"],
},
};
export default defineConfig<Options>({
projects: [
{
name: "Chrome",
use: {
...chromeProject,
},
},
{
name: "Firefox",
use: {
...devices["Desktop Firefox"],
launchOptions: {
firefoxUserPrefs: {
"permissions.default.microphone": 1,
},
},
// This is needed to work around an issue between Playwright routes, Firefox, and Service workers
// https://github.com/microsoft/playwright/issues/33561#issuecomment-2471642120
serviceWorkers: "block",
},
ignoreSnapshots: true,
},
{
name: "WebKit",
use: {
...devices["Desktop Safari"],
// Seemingly WebKit has the same issue as Firefox in Playwright routes not working
// https://playwright.dev/docs/network#missing-network-events-and-service-workers
serviceWorkers: "block",
},
ignoreSnapshots: true,
},
{
name: "Dendrite",
use: {
...chromeProject,
homeserverType: "dendrite",
},
ignoreSnapshots: true,
},
{
name: "Pinecone",
use: {
...chromeProject,
homeserverType: "pinecone",
},
ignoreSnapshots: true,
},
],
use: {
viewport: { width: 1280, height: 720 },
ignoreHTTPSErrors: true,
video: "retain-on-failure",
baseURL,
permissions: ["clipboard-write", "clipboard-read", "microphone"],
launchOptions: {
args: ["--use-fake-ui-for-media-stream", "--use-fake-device-for-media-stream", "--mute-audio"],
},
trace: "on-first-retry",
},
webServer: {

View File

@@ -2,7 +2,7 @@
Copyright 2024 New Vector Ltd.
Copyright 2024 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/

View File

@@ -2,7 +2,7 @@
Copyright 2024 New Vector Ltd.
Copyright 2024 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
@@ -123,7 +123,7 @@ test.describe("Landmark navigation tests", () => {
await expect(page.getByText("Bob joined the room")).toBeVisible();
// Close the room
page.goto("/#/home");
await page.goto("/#/home");
// Pressing Control+F6 will first focus the space button
await page.keyboard.press("ControlOrMeta+F6");

View File

@@ -2,7 +2,7 @@
Copyright 2024 New Vector Ltd.
Copyright 2024 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/

View File

@@ -2,7 +2,7 @@
Copyright 2024 New Vector Ltd.
Copyright 2024 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
@@ -13,13 +13,8 @@ Please see LICENSE files in the repository root for full details.
import { expect, test } from "../../element-web-test";
test.use({
startHomeserverOpts: "guest-enabled",
config: async ({ homeserver }, use) => {
await use({
default_server_config: {
"m.homeserver": { base_url: homeserver.config.baseUrl },
},
});
synapseConfig: {
allow_guest_access: true,
},
});

View File

@@ -2,7 +2,7 @@
Copyright 2024 New Vector Ltd.
Copyright 2024 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/

View File

@@ -3,7 +3,7 @@ Copyright 2024 New Vector Ltd.
Copyright 2023 Suguru Hirahara
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
@@ -13,7 +13,15 @@ import { SettingLevel } from "../../../src/settings/SettingLevel";
import { Layout } from "../../../src/settings/enums/Layout";
import { ElementAppPage } from "../../pages/ElementAppPage";
test.describe("Audio player", () => {
// Find and click "Reply" button
const clickButtonReply = async (tile: Locator) => {
await expect(async () => {
await tile.hover();
await tile.getByRole("button", { name: "Reply", exact: true }).click();
}).toPass();
};
test.describe("Audio player", { tag: ["@no-firefox", "@no-webkit"] }, () => {
test.use({
displayName: "Hanako",
});
@@ -222,8 +230,7 @@ test.describe("Audio player", () => {
// Find and click "Reply" button on MessageActionBar
const tile = page.locator(".mx_EventTile_last");
await tile.hover();
await tile.getByRole("button", { name: "Reply", exact: true }).click();
await clickButtonReply(tile);
// Reply to the player with another audio file
await uploadFile(page, "playwright/sample-files/1sec.ogg");
@@ -251,19 +258,12 @@ test.describe("Audio player", () => {
const tile = page.locator(".mx_EventTile_last");
// Find and click "Reply" button
const clickButtonReply = async () => {
await tile.scrollIntoViewIfNeeded();
await tile.hover();
await tile.getByRole("button", { name: "Reply", exact: true }).click();
};
await uploadFile(page, "playwright/sample-files/upload-first.ogg");
// Assert that the audio player is rendered
await expect(page.locator(".mx_EventTile_last .mx_AudioPlayer_container")).toBeVisible();
await clickButtonReply();
await clickButtonReply(tile);
// Reply to the player with another audio file
await uploadFile(page, "playwright/sample-files/upload-second.ogg");
@@ -271,7 +271,7 @@ test.describe("Audio player", () => {
// Assert that the audio player is rendered
await expect(page.locator(".mx_EventTile_last .mx_AudioPlayer_container")).toBeVisible();
await clickButtonReply();
await clickButtonReply(tile);
// Reply to the player with yet another audio file to create a reply chain
await uploadFile(page, "playwright/sample-files/upload-third.ogg");

View File

@@ -2,7 +2,7 @@
Copyright 2024 New Vector Ltd.
Copyright 2024 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
@@ -95,7 +95,7 @@ test.describe("HTML Export", () => {
async ({ page, app, room }) => {
// Set a fixed time rather than masking off the line with the time in it: we don't need to worry
// about the width changing and we can actually test this line looks correct.
page.clock.setSystemTime(new Date("2024-01-01T00:00:00Z"));
await page.clock.setSystemTime(new Date("2024-01-01T00:00:00Z"));
// Send a bunch of messages to populate the room
for (let i = 1; i < 10; i++) {

View File

@@ -2,7 +2,7 @@
Copyright 2024 New Vector Ltd.
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/

View File

@@ -2,7 +2,7 @@
Copyright 2024 New Vector Ltd.
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
@@ -79,9 +79,8 @@ test.describe("Composer", () => {
// Enter some more text, then send the message
await page.getByRole("textbox").pressSequentially("this is the spoiler text ");
await page.getByRole("button", { name: "Send message" }).click();
// Check that a spoiler item has appeared in the timeline and locator the spoiler command text
await expect(page.locator("button.mx_EventTile_spoiler")).toBeVisible();
await expect(page.getByText("this is the spoiler text")).toBeVisible();
// Check that a spoiler item has appeared in the timeline and contains the spoiler text
await expect(page.locator("button.mx_EventTile_spoiler")).toHaveText("this is the spoiler text");
});
});
});
@@ -166,7 +165,7 @@ test.describe("Composer", () => {
// Type another
await page.locator("div[contenteditable=true]").pressSequentially("my message 1");
// Send message
page.locator("div[contenteditable=true]").press("Enter");
await page.locator("div[contenteditable=true]").press("Enter");
// It was sent
await expect(page.locator(".mx_EventTile_last .mx_EventTile_body").getByText("my message 1")).toBeVisible();
});

View File

@@ -2,7 +2,7 @@
Copyright 2024 New Vector Ltd.
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
@@ -27,7 +27,7 @@ test.describe("Create Room", () => {
// Submit
await dialog.getByRole("button", { name: "Create room" }).click();
await expect(page).toHaveURL(/\/#\/room\/#test-room-1:localhost/);
await expect(page).toHaveURL(new RegExp(`/#/room/#test-room-1:${user.homeServer}`));
const header = page.locator(".mx_RoomHeader");
await expect(header).toContainText(name);
});

View File

@@ -0,0 +1,95 @@
/*
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { test, expect } from "../../element-web-test";
import { registerAccountMas } from "../oidc";
import { isDendrite } from "../../plugins/homeserver/dendrite";
import { TestClientServerAPI } from "../csAPI";
import { masHomeserver } from "../../plugins/homeserver/synapse/masHomeserver.ts";
// These tests register an account with MAS because then we go through the "normal" registration flow
// and crypto gets set up. Using the 'user' fixture create a user and synthesizes an existing login,
// which is faster but leaves us without crypto set up.
test.use(masHomeserver);
test.describe("Encryption state after registration", () => {
test.skip(isDendrite, "does not yet support MAS");
test("Key backup is enabled by default", async ({ page, mailpitClient, app }, testInfo) => {
await page.goto("/#/login");
await page.getByRole("button", { name: "Continue" }).click();
await registerAccountMas(page, mailpitClient, `alice_${testInfo.testId}`, "alice@email.com", "Pa$sW0rD!");
await app.settings.openUserSettings("Security & Privacy");
await expect(page.getByText("This session is backing up your keys.")).toBeVisible();
});
test("user is prompted to set up recovery", async ({ page, mailpitClient, app }, testInfo) => {
await page.goto("/#/login");
await page.getByRole("button", { name: "Continue" }).click();
await registerAccountMas(page, mailpitClient, `alice_${testInfo.testId}`, "alice@email.com", "Pa$sW0rD!");
await page.getByRole("button", { name: "Add room" }).click();
await page.getByRole("menuitem", { name: "New room" }).click();
await page.getByRole("textbox", { name: "Name" }).fill("test room");
await page.getByRole("button", { name: "Create room" }).click();
await expect(page.getByRole("heading", { name: "Set up recovery" })).toBeVisible();
});
});
test.describe("Key backup reset from elsewhere", () => {
test.skip(isDendrite, "does not yet support MAS");
test("Key backup is disabled when reset from elsewhere", async ({
page,
mailpitClient,
request,
homeserver,
}, testInfo) => {
const testUsername = `alice_${testInfo.testId}`;
const testPassword = "Pa$sW0rD!";
// there's a delay before keys are uploaded so the error doesn't appear immediately: use a fake
// clock so we can skip the delay
await page.clock.install();
await page.goto("/#/login");
await page.getByRole("button", { name: "Continue" }).click();
await registerAccountMas(page, mailpitClient, testUsername, "alice@email.com", testPassword);
await page.getByRole("button", { name: "Add room" }).click();
await page.getByRole("menuitem", { name: "New room" }).click();
await page.getByRole("textbox", { name: "Name" }).fill("test room");
await page.getByRole("button", { name: "Create room" }).click();
const accessToken = await page.evaluate(() => window.mxMatrixClientPeg.get().getAccessToken());
const csAPI = new TestClientServerAPI(request, homeserver, accessToken);
const backupInfo = await csAPI.getCurrentBackupInfo();
await csAPI.deleteBackupVersion(backupInfo.version);
await page.getByRole("textbox", { name: "Send an encrypted message…" }).fill("/discardsession");
await page.getByRole("button", { name: "Send message" }).click();
await page.getByRole("textbox", { name: "Send an encrypted message…" }).fill("Message with broken key backup");
await page.getByRole("button", { name: "Send message" }).click();
// Should be the message we sent plus the room creation event
await expect(page.locator(".mx_EventTile")).toHaveCount(2);
await expect(
page.locator(".mx_RoomView_MessageList > .mx_EventTile_last .mx_EventTile_receiptSent"),
).toBeVisible();
// Wait for it to try uploading the key
await page.clock.fastForward(20000);
await expect(page.getByRole("heading", { level: 1, name: "New Recovery Method" })).toBeVisible();
});
});

View File

@@ -2,15 +2,15 @@
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { type Page } from "@playwright/test";
import { test, expect } from "../../element-web-test";
import { test as masTest, registerAccountMas } from "../oidc";
import { isDendrite } from "../../plugins/homeserver/dendrite";
import { completeCreateSecretStorageDialog } from "./utils.ts";
async function expectBackupVersionToBe(page: Page, version: string) {
await expect(page.locator(".mx_SecureBackupPanel_statusList tr:nth-child(5) td")).toHaveText(
@@ -20,121 +20,89 @@ async function expectBackupVersionToBe(page: Page, version: string) {
await expect(page.locator(".mx_SecureBackupPanel_statusList tr:nth-child(6) td")).toHaveText(version);
}
masTest.describe("Encryption state after registration", () => {
masTest.skip(isDendrite, "does not yet support MAS");
masTest("Key backup is enabled by default", async ({ page, mailhog, app }) => {
await page.goto("/#/login");
await page.getByRole("button", { name: "Continue" }).click();
await registerAccountMas(page, mailhog.api, "alice", "alice@email.com", "Pa$sW0rD!");
await app.settings.openUserSettings("Security & Privacy");
expect(page.getByText("This session is backing up your keys.")).toBeVisible();
});
masTest("user is prompted to set up recovery", async ({ page, mailhog, app }) => {
await page.goto("/#/login");
await page.getByRole("button", { name: "Continue" }).click();
await registerAccountMas(page, mailhog.api, "alice", "alice@email.com", "Pa$sW0rD!");
await page.getByRole("button", { name: "Add room" }).click();
await page.getByRole("menuitem", { name: "New room" }).click();
await page.getByRole("textbox", { name: "Name" }).fill("test room");
await page.getByRole("button", { name: "Create room" }).click();
await expect(page.getByRole("heading", { name: "Set up recovery" })).toBeVisible();
});
});
test.describe("Backups", () => {
test.skip(isDendrite, "Dendrite lacks support for MSC3967 so requires additional auth here");
test.use({
displayName: "Hanako",
});
test("Create, delete and recreate a keys backup", async ({ page, user, app }, workerInfo) => {
// Create a backup
const securityTab = await app.settings.openUserSettings("Security & Privacy");
test(
"Create, delete and recreate a keys backup",
{ tag: "@no-webkit" },
async ({ page, user, app }, workerInfo) => {
// Create a backup
const securityTab = await app.settings.openUserSettings("Security & Privacy");
await expect(securityTab.getByRole("heading", { name: "Secure Backup" })).toBeVisible();
await securityTab.getByRole("button", { name: "Set up", exact: true }).click();
await expect(securityTab.getByRole("heading", { name: "Secure Backup" })).toBeVisible();
await securityTab.getByRole("button", { name: "Set up", exact: true }).click();
const currentDialogLocator = page.locator(".mx_Dialog");
const securityKey = await completeCreateSecretStorageDialog(page);
// It's the first time and secure storage is not set up, so it will create one
await expect(currentDialogLocator.getByRole("heading", { name: "Set up Secure Backup" })).toBeVisible();
await currentDialogLocator.getByRole("button", { name: "Continue", exact: true }).click();
await expect(currentDialogLocator.getByRole("heading", { name: "Save your Security Key" })).toBeVisible();
await currentDialogLocator.getByRole("button", { name: "Copy", exact: true }).click();
// copy the recovery key to use it later
const securityKey = await app.getClipboard();
await currentDialogLocator.getByRole("button", { name: "Continue", exact: true }).click();
// Open the settings again
await app.settings.openUserSettings("Security & Privacy");
await expect(securityTab.getByRole("heading", { name: "Secure Backup" })).toBeVisible();
await expect(currentDialogLocator.getByRole("heading", { name: "Secure Backup successful" })).toBeVisible();
await currentDialogLocator.getByRole("button", { name: "Done", exact: true }).click();
// expand the advanced section to see the active version in the reports
await page
.locator(".mx_Dialog .mx_SettingsSubsection_content details .mx_SecureBackupPanel_advanced")
.locator("..")
.click();
// Open the settings again
await app.settings.openUserSettings("Security & Privacy");
await expect(securityTab.getByRole("heading", { name: "Secure Backup" })).toBeVisible();
await expectBackupVersionToBe(page, "1");
// expand the advanced section to see the active version in the reports
await page
.locator(".mx_Dialog .mx_SettingsSubsection_content details .mx_SecureBackupPanel_advanced")
.locator("..")
.click();
await securityTab.getByRole("button", { name: "Delete Backup", exact: true }).click();
const currentDialogLocator = page.locator(".mx_Dialog");
await expect(currentDialogLocator.getByRole("heading", { name: "Delete Backup" })).toBeVisible();
// Delete it
await currentDialogLocator.getByTestId("dialog-primary-button").click(); // Click "Delete Backup"
await expectBackupVersionToBe(page, "1");
// Create another
await securityTab.getByRole("button", { name: "Set up", exact: true }).click();
await expect(currentDialogLocator.getByRole("heading", { name: "Security Key" })).toBeVisible();
await currentDialogLocator.getByLabel("Security Key").fill(securityKey);
await currentDialogLocator.getByRole("button", { name: "Continue", exact: true }).click();
await securityTab.getByRole("button", { name: "Delete Backup", exact: true }).click();
await expect(currentDialogLocator.getByRole("heading", { name: "Delete Backup" })).toBeVisible();
// Delete it
await currentDialogLocator.getByTestId("dialog-primary-button").click(); // Click "Delete Backup"
// Should be successful
await expect(currentDialogLocator.getByRole("heading", { name: "Success!" })).toBeVisible();
await currentDialogLocator.getByRole("button", { name: "OK", exact: true }).click();
// Create another
await securityTab.getByRole("button", { name: "Set up", exact: true }).click();
await expect(currentDialogLocator.getByRole("heading", { name: "Security Key" })).toBeVisible();
await currentDialogLocator.getByLabel("Security Key").fill(securityKey);
await currentDialogLocator.getByRole("button", { name: "Continue", exact: true }).click();
// Open the settings again
await app.settings.openUserSettings("Security & Privacy");
await expect(securityTab.getByRole("heading", { name: "Secure Backup" })).toBeVisible();
// Should be successful
await expect(currentDialogLocator.getByRole("heading", { name: "Success!" })).toBeVisible();
await currentDialogLocator.getByRole("button", { name: "OK", exact: true }).click();
// expand the advanced section to see the active version in the reports
await page
.locator(".mx_Dialog .mx_SettingsSubsection_content details .mx_SecureBackupPanel_advanced")
.locator("..")
.click();
// Open the settings again
await app.settings.openUserSettings("Security & Privacy");
await expect(securityTab.getByRole("heading", { name: "Secure Backup" })).toBeVisible();
await expectBackupVersionToBe(page, "2");
// expand the advanced section to see the active version in the reports
await page
.locator(".mx_Dialog .mx_SettingsSubsection_content details .mx_SecureBackupPanel_advanced")
.locator("..")
.click();
// ==
// Ensure that if you don't have the secret storage passphrase the backup won't be created
// ==
await expectBackupVersionToBe(page, "2");
// First delete version 2
await securityTab.getByRole("button", { name: "Delete Backup", exact: true }).click();
await expect(currentDialogLocator.getByRole("heading", { name: "Delete Backup" })).toBeVisible();
// Click "Delete Backup"
await currentDialogLocator.getByTestId("dialog-primary-button").click();
// ==
// Ensure that if you don't have the secret storage passphrase the backup won't be created
// ==
// Try to create another
await securityTab.getByRole("button", { name: "Set up", exact: true }).click();
await expect(currentDialogLocator.getByRole("heading", { name: "Security Key" })).toBeVisible();
// But cancel the security key dialog, to simulate not having the secret storage passphrase
await currentDialogLocator.getByTestId("dialog-cancel-button").click();
// First delete version 2
await securityTab.getByRole("button", { name: "Delete Backup", exact: true }).click();
await expect(currentDialogLocator.getByRole("heading", { name: "Delete Backup" })).toBeVisible();
// Click "Delete Backup"
await currentDialogLocator.getByTestId("dialog-primary-button").click();
await expect(currentDialogLocator.getByRole("heading", { name: "Starting backup…" })).toBeVisible();
// check that it failed
await expect(currentDialogLocator.getByText("Unable to create key backup")).toBeVisible();
// cancel
await currentDialogLocator.getByTestId("dialog-cancel-button").click();
// Try to create another
await securityTab.getByRole("button", { name: "Set up", exact: true }).click();
await expect(currentDialogLocator.getByRole("heading", { name: "Security Key" })).toBeVisible();
// But cancel the security key dialog, to simulate not having the secret storage passphrase
await currentDialogLocator.getByTestId("dialog-cancel-button").click();
await expect(currentDialogLocator.getByRole("heading", { name: "Starting backup…" })).toBeVisible();
// check that it failed
await expect(currentDialogLocator.getByText("Unable to create key backup")).toBeVisible();
// cancel
await currentDialogLocator.getByTestId("dialog-cancel-button").click();
// go back to the settings to check that no backup was created (the setup button should still be there)
await app.settings.openUserSettings("Security & Privacy");
await expect(securityTab.getByRole("button", { name: "Set up", exact: true })).toBeVisible();
});
// go back to the settings to check that no backup was created (the setup button should still be there)
await app.settings.openUserSettings("Security & Privacy");
await expect(securityTab.getByRole("button", { name: "Set up", exact: true })).toBeVisible();
},
);
});

View File

@@ -2,14 +2,16 @@
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { test, expect } from "../../element-web-test";
import { logIntoElement } from "./utils";
import { isDendrite } from "../../plugins/homeserver/dendrite";
test.describe("Complete security", () => {
test.skip(isDendrite, "Dendrite lacks support for MSC3967 so requires additional auth here");
test.use({
displayName: "Jeff",
});
@@ -19,9 +21,9 @@ test.describe("Complete security", () => {
homeserver,
credentials,
}) => {
await logIntoElement(page, homeserver, credentials);
await logIntoElement(page, credentials);
await expect(page.getByText("Welcome Jeff", { exact: true })).toBeVisible();
});
// see also "Verify device during login with SAS" in `verifiction.spec.ts`.
// see also "Verify device during login with SAS" in `verification.spec.ts`.
});

View File

@@ -2,15 +2,23 @@
Copyright 2024 New Vector Ltd.
Copyright 2022-2024 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import type { Page } from "@playwright/test";
import { expect, test } from "../../element-web-test";
import { autoJoin, copyAndContinue, createSharedRoomWithUser, enableKeyBackup, verify } from "./utils";
import {
autoJoin,
completeCreateSecretStorageDialog,
copyAndContinue,
createSharedRoomWithUser,
enableKeyBackup,
verify,
} from "./utils";
import { Bot } from "../../pages/bot";
import { ElementAppPage } from "../../pages/ElementAppPage";
import { isDendrite } from "../../plugins/homeserver/dendrite";
const checkDMRoom = async (page: Page) => {
const body = page.locator(".mx_RoomView_body");
@@ -67,6 +75,7 @@ const bobJoin = async (page: Page, bob: Bot) => {
};
test.describe("Cryptography", function () {
test.skip(isDendrite, "Dendrite lacks support for MSC3967 so requires additional auth here");
test.use({
displayName: "Alice",
botCreateOpts: {
@@ -81,7 +90,7 @@ test.describe("Cryptography", function () {
* Verify that the `m.cross_signing.${keyType}` key is available on the account data on the server
* @param keyType
*/
async function verifyKey(app: ElementAppPage, keyType: string) {
async function verifyKey(app: ElementAppPage, keyType: "master" | "self_signing" | "user_signing") {
const accountData: { encrypted: Record<string, Record<string, string>> } = await app.client.evaluate(
(cli, keyType) => cli.getAccountDataFromServer(`m.cross_signing.${keyType}`),
keyType,
@@ -109,18 +118,7 @@ test.describe("Cryptography", function () {
await app.settings.openUserSettings("Security & Privacy");
await page.getByRole("button", { name: "Set up Secure Backup" }).click();
const dialog = page.locator(".mx_Dialog");
// Recovery key is selected by default
await dialog.getByRole("button", { name: "Continue" }).click();
await copyAndContinue(page);
// If the device is unverified, there should be a "Setting up keys" step; however, it
// can be quite quick, and playwright can miss it, so we can't test for it.
// Either way, we end up at a success dialog:
await expect(dialog.getByText("Secure Backup successful")).toBeVisible();
await dialog.getByRole("button", { name: "Done" }).click();
await expect(dialog.getByText("Secure Backup successful")).not.toBeVisible();
await completeCreateSecretStorageDialog(page);
// Verify that the SSSS keys are in the account data stored in the server
await verifyKey(app, "master");

View File

@@ -2,7 +2,7 @@
Copyright 2024 New Vector Ltd.
Copyright 2022-2024 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
@@ -28,6 +28,8 @@ test.describe("Cryptography", function () {
});
test.describe("decryption failure messages", () => {
test.skip(isDendrite, "Dendrite lacks support for MSC3967 so requires additional auth here");
test("should handle device-relative historical messages", async ({
homeserver,
page,
@@ -45,7 +47,7 @@ test.describe("Cryptography", function () {
await logOutOfElement(page, true);
// Log in again, and see how the message looks.
await logIntoElement(page, homeserver, credentials);
await logIntoElement(page, credentials);
await app.viewRoomByName("Test room");
const lastTile = page.locator(".mx_EventTile").last();
await expect(lastTile).toContainText("Historical messages are not available on this device");
@@ -62,7 +64,7 @@ test.describe("Cryptography", function () {
// Finally, log out again, and back in, skipping verification for now, and see what we see.
await logOutOfElement(page);
await logIntoElement(page, homeserver, credentials);
await logIntoElement(page, credentials);
await page.locator(".mx_AuthPage").getByRole("button", { name: "Skip verification for now" }).click();
await page.locator(".mx_AuthPage").getByRole("button", { name: "I'll verify later" }).click();
await app.viewRoomByName("Test room");

View File

@@ -2,26 +2,36 @@
Copyright 2024 New Vector Ltd.
Copyright 2024 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { Locator, type Page } from "@playwright/test";
import { test as base, expect, Fixtures } from "../../element-web-test";
import { test, expect } from "../../element-web-test";
import { viewRoomSummaryByName } from "../right-panel/utils";
import { isDendrite } from "../../plugins/homeserver/dendrite";
import { completeCreateSecretStorageDialog, createBot, logIntoElement } from "./utils.ts";
import { Client } from "../../pages/client.ts";
const test = base.extend<Fixtures>({
// eslint-disable-next-line no-empty-pattern
startHomeserverOpts: async ({}, use) => {
await use("dehydration");
const ROOM_NAME = "Test room";
const NAME = "Alice";
function getMemberTileByName(page: Page, name: string): Locator {
return page.locator(`.mx_MemberTileView, [title="${name}"]`);
}
test.use({
displayName: NAME,
synapseConfig: {
experimental_features: {
msc2697_enabled: false,
msc3814_enabled: true,
},
},
config: async ({ homeserver, context }, use) => {
config: async ({ config, context }, use) => {
const wellKnown = {
"m.homeserver": {
base_url: homeserver.config.baseUrl,
},
...config.default_server_config,
"org.matrix.msc3814": true,
};
@@ -29,27 +39,14 @@ const test = base.extend<Fixtures>({
await route.fulfill({ json: wellKnown });
});
await use({
default_server_config: wellKnown,
});
await use(config);
},
});
const ROOM_NAME = "Test room";
const NAME = "Alice";
function getMemberTileByName(page: Page, name: string): Locator {
return page.locator(`.mx_EntityTile, [title="${name}"]`);
}
test.describe("Dehydration", () => {
test.skip(isDendrite, "does not yet support dehydration v2");
test.use({
displayName: NAME,
});
test("Create dehydrated device", async ({ page, user, app }, workerInfo) => {
test("'Set up secure backup' creates dehydrated device", async ({ page, user, app }, workerInfo) => {
// Create a backup (which will create SSSS, and dehydrated device)
const securityTab = await app.settings.openUserSettings("Security & Privacy");
@@ -58,17 +55,7 @@ test.describe("Dehydration", () => {
await expect(securityTab.getByText("Offline device enabled")).not.toBeVisible();
await securityTab.getByRole("button", { name: "Set up", exact: true }).click();
const currentDialogLocator = page.locator(".mx_Dialog");
// It's the first time and secure storage is not set up, so it will create one
await expect(currentDialogLocator.getByRole("heading", { name: "Set up Secure Backup" })).toBeVisible();
await currentDialogLocator.getByRole("button", { name: "Continue", exact: true }).click();
await expect(currentDialogLocator.getByRole("heading", { name: "Save your Security Key" })).toBeVisible();
await currentDialogLocator.getByRole("button", { name: "Copy", exact: true }).click();
await currentDialogLocator.getByRole("button", { name: "Continue", exact: true }).click();
await expect(currentDialogLocator.getByRole("heading", { name: "Secure Backup successful" })).toBeVisible();
await currentDialogLocator.getByRole("button", { name: "Done", exact: true }).click();
await completeCreateSecretStorageDialog(page);
// Open the settings again
await app.settings.openUserSettings("Security & Privacy");
@@ -93,7 +80,7 @@ test.describe("Dehydration", () => {
await viewRoomSummaryByName(page, app, ROOM_NAME);
await page.locator(".mx_RightPanel").getByRole("menuitem", { name: "People" }).click();
await expect(page.locator(".mx_MemberList")).toBeVisible();
await expect(page.locator(".mx_MemberListView")).toBeVisible();
await getMemberTileByName(page, NAME).click();
await page.locator(".mx_UserInfo_devices .mx_UserInfo_expand").click();
@@ -101,4 +88,49 @@ test.describe("Dehydration", () => {
await expect(page.locator(".mx_UserInfo_devices").getByText("Offline device enabled")).toBeVisible();
await expect(page.locator(".mx_UserInfo_devices").getByText("Dehydrated device")).not.toBeVisible();
});
test("Reset recovery key during login re-creates dehydrated device", async ({
page,
homeserver,
app,
credentials,
}) => {
// Set up cross-signing and recovery
const { botClient } = await createBot(page, homeserver, credentials);
// ... and dehydration
await botClient.evaluate(async (client) => await client.getCrypto().startDehydration());
const initialDehydratedDeviceIds = await getDehydratedDeviceIds(botClient);
expect(initialDehydratedDeviceIds.length).toBe(1);
await botClient.evaluate(async (client) => client.stopClient());
// Log in our client
await logIntoElement(page, credentials);
// Oh no, we forgot our recovery key
await page.locator(".mx_AuthPage").getByRole("button", { name: "Reset all" }).click();
await page.locator(".mx_AuthPage").getByRole("button", { name: "Proceed with reset" }).click();
await completeCreateSecretStorageDialog(page, { accountPassword: credentials.password });
// There should be a brand new dehydrated device
const dehydratedDeviceIds = await getDehydratedDeviceIds(app.client);
expect(dehydratedDeviceIds.length).toBe(1);
expect(dehydratedDeviceIds[0]).not.toEqual(initialDehydratedDeviceIds[0]);
});
});
async function getDehydratedDeviceIds(client: Client): Promise<string[]> {
return await client.evaluate(async (client) => {
const userId = client.getUserId();
const devices = await client.getCrypto().getUserDeviceInfo([userId]);
return Array.from(
devices
.get(userId)
.values()
.filter((d) => d.dehydrated)
.map((d) => d.deviceId),
);
});
}

View File

@@ -2,7 +2,7 @@
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
@@ -15,42 +15,23 @@ import {
awaitVerifier,
checkDeviceIsConnectedKeyBackup,
checkDeviceIsCrossSigned,
createBot,
doTwoWaySasVerification,
logIntoElement,
waitForVerificationRequest,
} from "./utils";
import { Bot } from "../../pages/bot";
test.describe("Device verification", () => {
test.describe("Device verification", { tag: "@no-webkit" }, () => {
let aliceBotClient: Bot;
/** The backup version that was set up by the bot client. */
let expectedBackupVersion: string;
test.beforeEach(async ({ page, homeserver, credentials }) => {
// Visit the login page of the app, to load the matrix sdk
await page.goto("/#/login");
// wait for the page to load
await page.waitForSelector(".mx_AuthPage", { timeout: 30000 });
// Create a new device for alice
aliceBotClient = new Bot(page, homeserver, {
bootstrapCrossSigning: true,
bootstrapSecretStorage: true,
});
aliceBotClient.setCredentials(credentials);
// Backup is prepared in the background. Poll until it is ready.
const botClientHandle = await aliceBotClient.prepareClient();
await expect
.poll(async () => {
expectedBackupVersion = await botClientHandle.evaluate((cli) =>
cli.getCrypto()!.getActiveSessionBackupVersion(),
);
return expectedBackupVersion;
})
.not.toBe(null);
const res = await createBot(page, homeserver, credentials);
aliceBotClient = res.botClient;
expectedBackupVersion = res.expectedBackupVersion;
});
// Click the "Verify with another device" button, and have the bot client auto-accept it.
@@ -66,7 +47,7 @@ test.describe("Device verification", () => {
}
test("Verify device with SAS during login", async ({ page, app, credentials, homeserver }) => {
await logIntoElement(page, homeserver, credentials);
await logIntoElement(page, credentials);
// Launch the verification request between alice and the bot
const verificationRequest = await initiateAliceVerificationRequest(page);
@@ -87,13 +68,13 @@ test.describe("Device verification", () => {
// Check that the current device is connected to key backup
// For now we don't check that the backup key is in cache because it's a bit flaky,
// as we need to wait for the secret gossiping to happen and the settings dialog doesn't refresh automatically.
await checkDeviceIsConnectedKeyBackup(page, expectedBackupVersion, false);
// as we need to wait for the secret gossiping to happen.
await checkDeviceIsConnectedKeyBackup(app, expectedBackupVersion, false);
});
test("Verify device with QR code during login", async ({ page, app, credentials, homeserver }) => {
// A mode 0x02 verification: "self-verifying in which the current device does not yet trust the master key"
await logIntoElement(page, homeserver, credentials);
await logIntoElement(page, credentials);
// Launch the verification request between alice and the bot
const verificationRequest = await initiateAliceVerificationRequest(page);
@@ -131,13 +112,11 @@ test.describe("Device verification", () => {
await checkDeviceIsCrossSigned(app);
// Check that the current device is connected to key backup
// For now we don't check that the backup key is in cache because it's a bit flaky,
// as we need to wait for the secret gossiping to happen and the settings dialog doesn't refresh automatically.
await checkDeviceIsConnectedKeyBackup(page, expectedBackupVersion, false);
await checkDeviceIsConnectedKeyBackup(app, expectedBackupVersion, true);
});
test("Verify device with Security Phrase during login", async ({ page, app, credentials, homeserver }) => {
await logIntoElement(page, homeserver, credentials);
await logIntoElement(page, credentials);
// Select the security phrase
await page.locator(".mx_AuthPage").getByRole("button", { name: "Verify with Security Key or Phrase" }).click();
@@ -154,11 +133,11 @@ test.describe("Device verification", () => {
// Check that the current device is connected to key backup
// The backup decryption key should be in cache also, as we got it directly from the 4S
await checkDeviceIsConnectedKeyBackup(page, expectedBackupVersion, true);
await checkDeviceIsConnectedKeyBackup(app, expectedBackupVersion, true);
});
test("Verify device with Security Key during login", async ({ page, app, credentials, homeserver }) => {
await logIntoElement(page, homeserver, credentials);
await logIntoElement(page, credentials);
// Select the security phrase
await page.locator(".mx_AuthPage").getByRole("button", { name: "Verify with Security Key or Phrase" }).click();
@@ -177,11 +156,11 @@ test.describe("Device verification", () => {
// Check that the current device is connected to key backup
// The backup decryption key should be in cache also, as we got it directly from the 4S
await checkDeviceIsConnectedKeyBackup(page, expectedBackupVersion, true);
await checkDeviceIsConnectedKeyBackup(app, expectedBackupVersion, true);
});
test("Handle incoming verification request with SAS", async ({ page, credentials, homeserver, toasts }) => {
await logIntoElement(page, homeserver, credentials);
await logIntoElement(page, credentials);
/* Dismiss "Verify this device" */
const authPage = page.locator(".mx_AuthPage");
@@ -212,16 +191,17 @@ test.describe("Device verification", () => {
/* on the bot side, wait for the verifier to exist ... */
const verifier = await awaitVerifier(botVerificationRequest);
// ... confirm ...
botVerificationRequest.evaluate((verificationRequest) => verificationRequest.verifier.verify());
void botVerificationRequest.evaluate((verificationRequest) => verificationRequest.verifier.verify());
// ... and then check the emoji match
await doTwoWaySasVerification(page, verifier);
/* And we're all done! */
const infoDialog = page.locator(".mx_InfoDialog");
await infoDialog.getByRole("button", { name: "They match" }).click();
await expect(
infoDialog.getByText(`You've successfully verified (${aliceBotClient.credentials.deviceId})!`),
).toBeVisible();
// We don't assert the full string as the device name is unset on Synapse but set to the user ID on Dendrite
await expect(infoDialog.getByText(`You've successfully verified`)).toContainText(
`(${aliceBotClient.credentials.deviceId})`,
);
await infoDialog.getByRole("button", { name: "Got it" }).click();
});
});

View File

@@ -2,7 +2,7 @@
Copyright 2024 New Vector Ltd.
Copyright 2022-2024 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
@@ -53,6 +53,8 @@ test.describe("Cryptography", function () {
// Even though Alice has seen Bob's join event, Bob may not have done so yet. Wait for the sync to arrive.
await bob.awaitRoomMembership(testRoomId);
await app.client.network.setupRoute();
});
test("should show the correct shield on e2e events", async ({
@@ -64,6 +66,9 @@ test.describe("Cryptography", function () {
// Bob has a second, not cross-signed, device
const bobSecondDevice = await createSecondBotDevice(page, homeserver, bob);
// Dismiss the toast nagging us to set up recovery otherwise it gets in the way of clicking the room list
await page.getByRole("button", { name: "Not now" }).click();
await bob.sendEvent(testRoomId, null, "m.room.encrypted", {
algorithm: "m.megolm.v1.aes-sha2",
ciphertext: "the bird is in the hand",
@@ -205,7 +210,7 @@ test.describe("Cryptography", function () {
window.localStorage.clear();
});
await page.reload();
await logIntoElement(page, homeserver, aliceCredentials, securityKey);
await logIntoElement(page, aliceCredentials, securityKey);
/* go back to the test room and find Bob's message again */
await app.viewRoomById(testRoomId);

View File

@@ -1,7 +1,7 @@
/*
Copyright 2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
@@ -11,6 +11,7 @@ import { bootstrapCrossSigningForClient } from "../../pages/client.ts";
/** Tests for the "invisible crypto" behaviour -- i.e., when the "exclude insecure devices" setting is enabled */
test.describe("Invisible cryptography", () => {
test.slow();
test.use({
displayName: "Alice",
botCreateOpts: { displayName: "Bob" },

View File

@@ -2,16 +2,18 @@
Copyright 2024 New Vector Ltd.
Copyright 2024 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { test, expect } from "../../element-web-test";
import { createRoom, enableKeyBackup, logIntoElement, sendMessageInCurrentRoom } from "./utils";
import { isDendrite } from "../../plugins/homeserver/dendrite";
test.describe("Logout tests", () => {
test.skip(isDendrite, "Dendrite lacks support for MSC3967 so requires additional auth here");
test.beforeEach(async ({ page, homeserver, credentials }) => {
await logIntoElement(page, homeserver, credentials);
await logIntoElement(page, credentials);
});
test("Ask to set up recovery on logout if not setup", async ({ page, app }) => {

View File

@@ -2,31 +2,31 @@
Copyright 2024 New Vector Ltd.
Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import path from "path";
import { readFile } from "node:fs/promises";
import { expect, Fixtures, test as base } from "../../element-web-test";
import { expect, test } from "../../element-web-test";
const test = base.extend<Fixtures>({
// Replace the `user` fixture with one which populates the indexeddb data before starting the app.
user: async ({ context, pageWithCredentials: page, credentials }, use) => {
await page.route(`/test_indexeddb_cryptostore_dump/*`, async (route, request) => {
const resourcePath = path.join(__dirname, new URL(request.url()).pathname);
const body = await readFile(resourcePath, { encoding: "utf-8" });
await route.fulfill({ body });
});
await page.goto("/test_indexeddb_cryptostore_dump/index.html");
test.describe("migration", { tag: "@no-webkit" }, function () {
test.use({
displayName: "Alice",
await use(credentials);
},
});
// Replace the `user` fixture with one which populates the indexeddb data before starting the app.
user: async ({ context, pageWithCredentials: page, credentials }, use) => {
await page.route(`/test_indexeddb_cryptostore_dump/*`, async (route, request) => {
const resourcePath = path.join(__dirname, new URL(request.url()).pathname);
const body = await readFile(resourcePath, { encoding: "utf-8" });
await route.fulfill({ body });
});
await page.goto("/test_indexeddb_cryptostore_dump/index.html");
test.describe("migration", function () {
test.use({ displayName: "Alice" });
await use(credentials);
},
});
test("Should support migration from legacy crypto", async ({ context, user, page }, workerInfo) => {
test.slow();

View File

@@ -2,7 +2,7 @@
Copyright 2024 New Vector Ltd.
Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/

View File

@@ -2,7 +2,7 @@
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
@@ -74,7 +74,7 @@ test.describe("User verification", () => {
/* on the bot side, wait for the verifier to exist ... */
const botVerifier = await awaitVerifier(bobVerificationRequest);
// ... confirm ...
botVerifier.evaluate((verifier) => verifier.verify());
void botVerifier.evaluate((verifier) => verifier.verify());
// ... and then check the emoji match
await doTwoWaySasVerification(page, botVerifier);

View File

@@ -2,7 +2,7 @@
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
@@ -12,6 +12,7 @@ import type { ICreateRoomOpts, MatrixClient } from "matrix-js-sdk/src/matrix";
import type {
CryptoEvent,
EmojiMapping,
GeneratedSecretStorageKey,
ShowSasCallbacks,
VerificationRequest,
Verifier,
@@ -22,6 +23,46 @@ import { Client } from "../../pages/client";
import { ElementAppPage } from "../../pages/ElementAppPage";
import { Bot } from "../../pages/bot";
/**
* Create a bot client using the supplied credentials, and wait for the key backup to be ready.
* @param page - the playwright `page` fixture
* @param homeserver - the homeserver to use
* @param credentials - the credentials to use for the bot client
*/
export async function createBot(
page: Page,
homeserver: HomeserverInstance,
credentials: Credentials,
): Promise<{ botClient: Bot; recoveryKey: GeneratedSecretStorageKey; expectedBackupVersion: string }> {
// Visit the login page of the app, to load the matrix sdk
await page.goto("/#/login");
// wait for the page to load
await page.waitForSelector(".mx_AuthPage", { timeout: 30000 });
// Create a new bot client
const botClient = new Bot(page, homeserver, {
bootstrapCrossSigning: true,
bootstrapSecretStorage: true,
});
botClient.setCredentials(credentials);
// Backup is prepared in the background. Poll until it is ready.
const botClientHandle = await botClient.prepareClient();
let expectedBackupVersion: string;
await expect
.poll(async () => {
expectedBackupVersion = await botClientHandle.evaluate((cli) =>
cli.getCrypto()!.getActiveSessionBackupVersion(),
);
return expectedBackupVersion;
})
.not.toBe(null);
const recoveryKey = await botClient.getRecoveryKey();
return { botClient, recoveryKey, expectedBackupVersion };
}
/**
* wait for the given client to receive an incoming verification request, and automatically accept it
*
@@ -59,7 +100,7 @@ export function handleSasVerification(verifier: JSHandle<Verifier>): Promise<Emo
return new Promise<EmojiMapping[]>((resolve) => {
const onShowSas = (event: ShowSasCallbacks) => {
verifier.off("show_sas" as VerifierEvent, onShowSas);
event.confirm();
void event.confirm();
resolve(event.sas.emoji);
};
@@ -98,14 +139,14 @@ export async function checkDeviceIsCrossSigned(app: ElementAppPage): Promise<voi
* Check that the current device is connected to the expected key backup.
* Also checks that the decryption key is known and cached locally.
*
* @param page - the page to check
* @param app -` ElementAppPage` wrapper for the playwright `Page`.
* @param expectedBackupVersion - the version of the backup we expect to be connected to.
* @param checkBackupKeyInCache - whether to check that the backup key is cached locally.
* @param checkBackupPrivateKeyInCache - whether to check that the backup decryption key is cached locally
*/
export async function checkDeviceIsConnectedKeyBackup(
page: Page,
app: ElementAppPage,
expectedBackupVersion: string,
checkBackupKeyInCache: boolean,
checkBackupPrivateKeyInCache: boolean,
): Promise<void> {
// Sanity check the given backup version: if it's null, something went wrong earlier in the test.
if (!expectedBackupVersion) {
@@ -114,23 +155,48 @@ export async function checkDeviceIsConnectedKeyBackup(
);
}
await page.getByRole("button", { name: "User menu" }).click();
await page.locator(".mx_UserMenu_contextMenu").getByRole("menuitem", { name: "Security & Privacy" }).click();
await expect(page.locator(".mx_Dialog").getByRole("button", { name: "Restore from Backup" })).toBeVisible();
const backupData = await app.client.evaluate(async (client: MatrixClient) => {
const crypto = client.getCrypto();
if (!crypto) return;
// expand the advanced section to see the active version in the reports
await page.locator(".mx_SecureBackupPanel_advanced").locator("..").click();
const backupInfo = await crypto.getKeyBackupInfo();
const backupKeyIn4S = Boolean(await client.isKeyBackupKeyStored());
const backupPrivateKeyFromCache = await crypto.getSessionBackupPrivateKey();
const hasBackupPrivateKeyFromCache = Boolean(backupPrivateKeyFromCache);
const backupPrivateKeyWellFormed = backupPrivateKeyFromCache instanceof Uint8Array;
const activeBackupVersion = await crypto.getActiveSessionBackupVersion();
if (checkBackupKeyInCache) {
const cacheDecryptionKeyStatusElement = page.locator(".mx_SecureBackupPanel_statusList tr:nth-child(2) td");
await expect(cacheDecryptionKeyStatusElement).toHaveText("cached locally, well formed");
return {
backupInfo,
hasBackupPrivateKeyFromCache,
backupPrivateKeyWellFormed,
backupKeyIn4S,
activeBackupVersion,
};
});
if (!backupData) {
throw new Error("Crypto module is not available");
}
await expect(page.locator(".mx_SecureBackupPanel_statusList tr:nth-child(5) td")).toHaveText(
expectedBackupVersion + " (Algorithm: m.megolm_backup.v1.curve25519-aes-sha2)",
);
const { backupInfo, backupKeyIn4S, hasBackupPrivateKeyFromCache, backupPrivateKeyWellFormed, activeBackupVersion } =
backupData;
await expect(page.locator(".mx_SecureBackupPanel_statusList tr:nth-child(6) td")).toHaveText(expectedBackupVersion);
// We have a key backup
expect(backupInfo).toBeDefined();
// The key backup version is as expected
expect(backupInfo.version).toBe(expectedBackupVersion);
// The active backup version is as expected
expect(activeBackupVersion).toBe(expectedBackupVersion);
// The backup key is stored in 4S
expect(backupKeyIn4S).toBe(true);
if (checkBackupPrivateKeyInCache) {
// The backup key is available locally
expect(hasBackupPrivateKeyFromCache).toBe(true);
// The backup key is well-formed
expect(backupPrivateKeyWellFormed).toBe(true);
}
}
/**
@@ -138,22 +204,9 @@ export async function checkDeviceIsConnectedKeyBackup(
*
* If a `securityKey` is given, verifies the new device using the key.
*/
export async function logIntoElement(
page: Page,
homeserver: HomeserverInstance,
credentials: Credentials,
securityKey?: string,
) {
export async function logIntoElement(page: Page, credentials: Credentials, securityKey?: string) {
await page.goto("/#/login");
// select homeserver
await page.getByRole("button", { name: "Edit" }).click();
await page.getByRole("textbox", { name: "Other homeserver" }).fill(homeserver.config.baseUrl);
await page.getByRole("button", { name: "Continue", exact: true }).click();
// wait for the dialog to go away
await expect(page.locator(".mx_ServerPickerDialog")).not.toBeVisible();
await page.getByRole("textbox", { name: "Username" }).fill(credentials.userId);
await page.getByPlaceholder("Password").fill(credentials.password);
await page.getByRole("button", { name: "Sign in" }).click();
@@ -188,18 +241,19 @@ export async function logOutOfElement(page: Page, discardKeys: boolean = false)
}
/**
* Open the security settings, and verify the current session using the security key.
* Open the encryption settings, and verify the current session using the security key.
*
* @param app - `ElementAppPage` wrapper for the playwright `Page`.
* @param securityKey - The security key (i.e., 4S key), set up during a previous session.
*/
export async function verifySession(app: ElementAppPage, securityKey: string) {
const settings = await app.settings.openUserSettings("Security & Privacy");
await settings.getByRole("button", { name: "Verify this session" }).click();
const settings = await app.settings.openUserSettings("Encryption");
await settings.getByRole("button", { name: "Verify this device" }).click();
await app.page.getByRole("button", { name: "Verify with Security Key" }).click();
await app.page.locator(".mx_Dialog").locator('input[type="password"]').fill(securityKey);
await app.page.getByRole("button", { name: "Continue", disabled: false }).click();
await app.page.getByRole("button", { name: "Done" }).click();
await app.settings.closeDialog();
}
/**
@@ -234,19 +288,52 @@ export async function doTwoWaySasVerification(page: Page, verifier: JSHandle<Ver
export async function enableKeyBackup(app: ElementAppPage): Promise<string> {
await app.settings.openUserSettings("Security & Privacy");
await app.page.getByRole("button", { name: "Set up Secure Backup" }).click();
const dialog = app.page.locator(".mx_Dialog");
// Recovery key is selected by default
await dialog.getByRole("button", { name: "Continue" }).click({ timeout: 60000 });
// copy the text ourselves
const securityKey = await dialog.locator(".mx_CreateSecretStorageDialog_recoveryKey code").textContent();
await copyAndContinue(app.page);
return await completeCreateSecretStorageDialog(app.page);
}
await expect(dialog.getByText("Secure Backup successful")).toBeVisible();
await dialog.getByRole("button", { name: "Done" }).click();
await expect(dialog.getByText("Secure Backup successful")).not.toBeVisible();
/**
* Go through the "Set up Secure Backup" dialog (aka the `CreateSecretStorageDialog`).
*
* Assumes the dialog is already open for some reason (see also {@link enableKeyBackup}).
*
* @param page - The playwright `Page` fixture.
* @param opts - Options object
* @param opts.accountPassword - The user's account password. If we are also resetting cross-signing, then we will need
* to upload the public cross-signing keys, which will cause the app to prompt for the password.
*
* @returns the new recovery key.
*/
export async function completeCreateSecretStorageDialog(
page: Page,
opts?: { accountPassword?: string },
): Promise<string> {
const currentDialogLocator = page.locator(".mx_Dialog");
return securityKey;
await expect(currentDialogLocator.getByRole("heading", { name: "Set up Secure Backup" })).toBeVisible();
// "Generate a Security Key" is selected by default
await currentDialogLocator.getByRole("button", { name: "Continue", exact: true }).click();
await expect(currentDialogLocator.getByRole("heading", { name: "Save your Security Key" })).toBeVisible();
await currentDialogLocator.getByRole("button", { name: "Copy", exact: true }).click();
// copy the recovery key to use it later
const recoveryKey = await page.evaluate(() => navigator.clipboard.readText());
await currentDialogLocator.getByRole("button", { name: "Continue", exact: true }).click();
// If the device is unverified, there should be a "Setting up keys" step.
// If this is not the first time we are setting up cross-signing, the app will prompt for our password; otherwise
// the step is quite quick, and playwright can miss it, so we can't test for it.
if (opts && Object.hasOwn(opts, "accountPassword")) {
await expect(currentDialogLocator.getByRole("heading", { name: "Setting up keys" })).toBeVisible();
await page.getByPlaceholder("Password").fill(opts!.accountPassword);
await currentDialogLocator.getByRole("button", { name: "Continue" }).click();
}
// Either way, we end up at a success dialog:
await expect(currentDialogLocator.getByRole("heading", { name: "Secure Backup successful" })).toBeVisible();
await currentDialogLocator.getByRole("button", { name: "Done", exact: true }).click();
await expect(currentDialogLocator.getByText("Secure Backup successful")).not.toBeVisible();
return recoveryKey;
}
/**
@@ -326,7 +413,7 @@ export async function autoJoin(client: Client) {
await client.evaluate((cli) => {
cli.on(window.matrixcs.RoomMemberEvent.Membership, (event, member) => {
if (member.membership === "invite" && member.userId === cli.getUserId()) {
cli.joinRoom(member.roomId);
void cli.joinRoom(member.roomId);
}
});
});
@@ -385,3 +472,25 @@ export async function createSecondBotDevice(page: Page, homeserver: HomeserverIn
await bobSecondDevice.prepareClient();
return bobSecondDevice;
}
/**
* Remove the cached secrets from the indexedDB
* This is a workaround to simulate the case where the secrets are not cached.
*/
export async function deleteCachedSecrets(page: Page) {
await page.evaluate(async () => {
const removeCachedSecrets = new Promise((resolve) => {
const request = window.indexedDB.open("matrix-js-sdk::matrix-sdk-crypto");
request.onsuccess = (event: Event & { target: { result: IDBDatabase } }) => {
const db = event.target.result;
const request = db.transaction("core", "readwrite").objectStore("core").delete("private_identity");
request.onsuccess = () => {
db.close();
resolve(undefined);
};
};
});
await removeCachedSecrets;
});
await page.reload();
}

39
playwright/e2e/csAPI.ts Normal file
View File

@@ -0,0 +1,39 @@
/*
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 { APIRequestContext } from "playwright-core";
import { KeyBackupInfo } from "matrix-js-sdk/src/crypto-api";
import { HomeserverInstance } from "../plugins/homeserver";
import { ClientServerApi } from "../plugins/utils/api.ts";
/**
* A small subset of the Client-Server API used to manipulate the state of the
* account on the homeserver independently of the client under test.
*/
export class TestClientServerAPI extends ClientServerApi {
public constructor(
request: APIRequestContext,
homeserver: HomeserverInstance,
private accessToken: string,
) {
super(homeserver.baseUrl);
this.setRequest(request);
}
public async getCurrentBackupInfo(): Promise<KeyBackupInfo | null> {
return this.request("GET", `/v3/room_keys/version`, this.accessToken);
}
/**
* Calls the API directly to delete the given backup version
* @param version The version to delete
*/
public async deleteBackupVersion(version: string): Promise<void> {
await this.request("DELETE", `/v3/room_keys/version/${version}`, this.accessToken);
}
}

View File

@@ -2,7 +2,7 @@
Copyright 2024 New Vector Ltd.
Copyright 2022 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
@@ -12,6 +12,7 @@ import type { EventType, IContent, ISendEventResponse, MsgType, Visibility } fro
import { expect, test } from "../../element-web-test";
import { ElementAppPage } from "../../pages/ElementAppPage";
import { SettingLevel } from "../../../src/settings/SettingLevel";
import { isDendrite } from "../../plugins/homeserver/dendrite";
async function sendEvent(app: ElementAppPage, roomId: string): Promise<ISendEventResponse> {
return app.client.sendEvent(roomId, null, "m.room.message" as EventType, {
@@ -31,6 +32,8 @@ function mkPadding(n: number): IContent {
}
test.describe("Editing", () => {
test.skip(isDendrite, "due to a Dendrite bug https://github.com/element-hq/dendrite/issues/3488");
// Edit "Message"
const editLastMessage = async (page: Page, edit: string) => {
const eventTile = page.locator(".mx_RoomView_MessageList .mx_EventTile_last");

View File

@@ -2,7 +2,7 @@
Copyright 2024 New Vector Ltd.
Copyright 2024 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/

View File

@@ -2,29 +2,44 @@
Copyright 2024 New Vector Ltd.
Copyright 2024 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { expect, test } from "../../element-web-test";
import { expect, test as base } from "../../element-web-test";
import { selectHomeserver } from "../utils";
import { emailHomeserver } from "../../plugins/homeserver/synapse/emailHomeserver.ts";
import { isDendrite } from "../../plugins/homeserver/dendrite";
import { Credentials } from "../../plugins/homeserver";
const username = "user1234";
// this has to be password-like enough to please zxcvbn. Needless to say it's just from pwgen.
const password = "oETo7MPf0o";
const email = "user@nowhere.dummy";
const test = base.extend<{ credentials: Pick<Credentials, "username" | "password"> }>({
// eslint-disable-next-line no-empty-pattern
credentials: async ({}, use, testInfo) => {
await use({
username: `user_${testInfo.testId}`,
// this has to be password-like enough to please zxcvbn. Needless to say it's just from pwgen.
password: "oETo7MPf0o",
});
},
});
test.use(emailHomeserver);
test.use({
config: {
// The only thing that we really *need* (otherwise Element refuses to load) is a default homeserver.
// We point that to a guaranteed-invalid domain.
default_server_config: {
"m.homeserver": {
base_url: "https://server.invalid",
},
},
},
});
test.describe("Forgot Password", () => {
test.use({
startHomeserverOpts: ({ mailhog }, use) =>
use({
template: "email",
variables: {
SMTP_HOST: "host.containers.internal",
SMTP_PORT: mailhog.instance.smtpPort,
},
}),
});
test.skip(isDendrite, "not yet wired up");
test("renders properly", { tag: "@screenshot" }, async ({ page, homeserver }) => {
await page.goto("/");
@@ -32,38 +47,42 @@ test.describe("Forgot Password", () => {
await page.getByRole("link", { name: "Sign in" }).click();
// need to select a homeserver at this stage, before entering the forgot password flow
await selectHomeserver(page, homeserver.config.baseUrl);
await selectHomeserver(page, homeserver.baseUrl);
await page.getByRole("button", { name: "Forgot password?" }).click();
await expect(page.getByRole("main")).toMatchScreenshot("forgot-password.png");
});
test("renders email verification dialog properly", { tag: "@screenshot" }, async ({ page, homeserver }) => {
const user = await homeserver.registerUser(username, password);
test(
"renders email verification dialog properly",
{ tag: "@screenshot" },
async ({ page, homeserver, credentials }) => {
const user = await homeserver.registerUser(credentials.username, credentials.password);
await homeserver.setThreepid(user.userId, "email", email);
await homeserver.setThreepid(user.userId, "email", email);
await page.goto("/");
await page.goto("/");
await page.getByRole("link", { name: "Sign in" }).click();
await selectHomeserver(page, homeserver.config.baseUrl);
await page.getByRole("link", { name: "Sign in" }).click();
await selectHomeserver(page, homeserver.baseUrl);
await page.getByRole("button", { name: "Forgot password?" }).click();
await page.getByRole("button", { name: "Forgot password?" }).click();
await page.getByRole("textbox", { name: "Email address" }).fill(email);
await page.getByRole("textbox", { name: "Email address" }).fill(email);
await page.getByRole("button", { name: "Send email" }).click();
await page.getByRole("button", { name: "Send email" }).click();
await page.getByRole("button", { name: "Next" }).click();
await page.getByRole("button", { name: "Next" }).click();
await page.getByRole("textbox", { name: "New Password", exact: true }).fill(password);
await page.getByRole("textbox", { name: "Confirm new password", exact: true }).fill(password);
await page.getByRole("textbox", { name: "New Password", exact: true }).fill(credentials.password);
await page.getByRole("textbox", { name: "Confirm new password", exact: true }).fill(credentials.password);
await page.getByRole("button", { name: "Reset password" }).click();
await page.getByRole("button", { name: "Reset password" }).click();
await expect(page.getByRole("button", { name: "Resend" })).toBeInViewport();
await expect(page.getByRole("button", { name: "Resend" })).toBeInViewport();
await expect(page.locator(".mx_Dialog")).toMatchScreenshot("forgot-password-verify-email.png");
});
await expect(page.locator(".mx_Dialog")).toMatchScreenshot("forgot-password-verify-email.png");
},
);
});

View File

@@ -2,13 +2,14 @@
Copyright 2024 New Vector Ltd.
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import type { Page } from "@playwright/test";
import { test, expect } from "../../element-web-test";
import { openIntegrationManager } from "./utils";
import type { UserWidget } from "../../../src/utils/WidgetUtils-types.ts";
const ROOM_NAME = "Integration Manager Test";
@@ -92,7 +93,7 @@ test.describe("Integration Manager: Get OpenID Token", () => {
},
},
id: "integration-manager",
},
} as unknown as UserWidget,
});
// Succeed when checking the token is valid

View File

@@ -2,13 +2,14 @@
Copyright 2024 New Vector Ltd.
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import type { Page } from "@playwright/test";
import { test, expect } from "../../element-web-test";
import { openIntegrationManager } from "./utils";
import type { UserWidget } from "../../../src/utils/WidgetUtils-types.ts";
const ROOM_NAME = "Integration Manager Test";
const USER_DISPLAY_NAME = "Alice";
@@ -68,29 +69,13 @@ async function sendActionFromIntegrationManager(
await iframe.getByRole("button", { name: "Press to send action" }).click();
}
async function clickUntilGone(page: Page, selector: string, attempt = 0) {
if (attempt === 11) {
throw new Error("clickUntilGone attempt count exceeded");
}
await page.locator(selector).last().click();
const count = await page.locator(selector).count();
if (count > 0) {
return clickUntilGone(page, selector, ++attempt);
}
}
async function expectKickedMessage(page: Page, shouldExist: boolean) {
// Expand any event summaries, we can't use a click multiple here because clicking one might de-render others
// This is quite horrible but seems the most stable way of clicking 0-N buttons,
// one at a time with a full re-evaluation after each click
await clickUntilGone(page, ".mx_GenericEventListSummary_toggle[aria-expanded=false]");
// Check for the event message (or lack thereof)
await expect(page.getByText(`${USER_DISPLAY_NAME} removed ${BOT_DISPLAY_NAME}: ${KICK_REASON}`)).toBeVisible({
visible: shouldExist,
});
await expect(async () => {
await page.locator(".mx_GenericEventListSummary_toggle[aria-expanded=false]").last().click();
await expect(page.getByText(`${USER_DISPLAY_NAME} removed ${BOT_DISPLAY_NAME}: ${KICK_REASON}`)).toBeVisible({
visible: shouldExist,
});
}).toPass();
}
test.describe("Integration Manager: Kick", () => {
@@ -136,7 +121,7 @@ test.describe("Integration Manager: Kick", () => {
},
},
id: "integration-manager",
},
} as unknown as UserWidget,
});
// Succeed when checking the token is valid

View File

@@ -2,13 +2,14 @@
Copyright 2024 New Vector Ltd.
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import type { Page } from "@playwright/test";
import { test, expect } from "../../element-web-test";
import { openIntegrationManager } from "./utils";
import type { UserWidget } from "../../../src/utils/WidgetUtils-types.ts";
const ROOM_NAME = "Integration Manager Test";
@@ -107,7 +108,7 @@ test.describe("Integration Manager: Read Events", () => {
},
},
id: "integration-manager",
},
} as unknown as UserWidget,
});
// Succeed when checking the token is valid

View File

@@ -2,13 +2,14 @@
Copyright 2024 New Vector Ltd.
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import type { Page } from "@playwright/test";
import { test, expect } from "../../element-web-test";
import { openIntegrationManager } from "./utils";
import type { UserWidget } from "../../../src/utils/WidgetUtils-types.ts";
const ROOM_NAME = "Integration Manager Test";
@@ -113,7 +114,7 @@ test.describe("Integration Manager: Send Event", () => {
},
},
id: "integration-manager",
},
} as unknown as UserWidget,
});
// Succeed when checking the token is valid

View File

@@ -2,7 +2,7 @@
Copyright 2024 New Vector Ltd.
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/

View File

@@ -3,7 +3,7 @@ Copyright 2024 New Vector Ltd.
Copyright 2023 Suguru Hirahara
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/

View File

@@ -2,15 +2,17 @@
Copyright 2024 New Vector Ltd.
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { test, expect } from "../../element-web-test";
import { waitForRoom } from "../utils";
import { Filter } from "../../pages/Spotlight";
import { isDendrite } from "../../plugins/homeserver/dendrite";
test.describe("Create Knock Room", () => {
test.skip(isDendrite, "Dendrite does not have support for knocking");
test.use({
displayName: "Alice",
labsFlags: ["feature_ask_to_join"],
@@ -79,6 +81,7 @@ test.describe("Create Knock Room", () => {
const spotlightDialog = await app.openSpotlight();
await spotlightDialog.filter(Filter.PublicRooms);
await spotlightDialog.search("Cyber");
await expect(spotlightDialog.results.nth(0)).toContainText("Cybersecurity");
});
});

View File

@@ -4,7 +4,7 @@ Copyright 2023 Mikhail Aheichyk
Copyright 2023 Nordeck IT + Consulting GmbH.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
@@ -13,8 +13,10 @@ import { type Visibility } from "matrix-js-sdk/src/matrix";
import { test, expect } from "../../element-web-test";
import { waitForRoom } from "../utils";
import { Filter } from "../../pages/Spotlight";
import { isDendrite } from "../../plugins/homeserver/dendrite";
test.describe("Knock Into Room", () => {
test.skip(isDendrite, "Dendrite does not have support for knocking");
test.use({
displayName: "Alice",
labsFlags: ["feature_ask_to_join"],
@@ -282,6 +284,7 @@ test.describe("Knock Into Room", () => {
const spotlightDialog = await app.openSpotlight();
await spotlightDialog.filter(Filter.PublicRooms);
await spotlightDialog.search("Cyber");
await expect(spotlightDialog.results.nth(0)).toContainText("Cybersecurity");
await spotlightDialog.results.nth(0).click();

View File

@@ -4,14 +4,16 @@ Copyright 2023 Mikhail Aheichyk
Copyright 2023 Nordeck IT + Consulting GmbH.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { test, expect } from "../../element-web-test";
import { waitForRoom } from "../utils";
import { isDendrite } from "../../plugins/homeserver/dendrite";
test.describe("Manage Knocks", () => {
test.skip(isDendrite, "Dendrite does not have support for knocking");
test.use({
displayName: "Alice",
labsFlags: ["feature_ask_to_join"],
@@ -50,7 +52,7 @@ test.describe("Manage Knocks", () => {
});
test("should deny knock using bar", async ({ page, app, bot, room }) => {
bot.knockRoom(room.roomId);
await bot.knockRoom(room.roomId);
const roomKnocksBar = page.locator(".mx_RoomKnocksBar");
await expect(roomKnocksBar.getByRole("heading", { name: "Asking to join" })).toBeVisible();

View File

@@ -2,7 +2,7 @@
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
@@ -10,8 +10,12 @@ import { Bot } from "../../pages/bot";
import type { Locator, Page } from "@playwright/test";
import type { ElementAppPage } from "../../pages/ElementAppPage";
import { test, expect } from "../../element-web-test";
import { Credentials } from "../../plugins/homeserver";
import { isDendrite } from "../../plugins/homeserver/dendrite";
test.describe("Lazy Loading", () => {
test.skip(isDendrite, "due to a Dendrite bug https://github.com/element-hq/dendrite/issues/3488");
const charlies: Bot[] = [];
test.use({
@@ -25,21 +29,28 @@ test.describe("Lazy Loading", () => {
});
});
test.beforeEach(async ({ page, homeserver, user, bot }) => {
test.beforeEach(async ({ page, homeserver, user, bot, app }) => {
for (let i = 1; i <= 10; i++) {
const displayName = `Charly #${i}`;
const bot = new Bot(page, homeserver, { displayName, startClient: false, autoAcceptInvites: false });
charlies.push(bot);
}
await app.client.network.setupRoute();
});
const name = "Lazy Loading Test";
const alias = "#lltest:localhost";
const charlyMsg1 = "hi bob!";
const charlyMsg2 = "how's it going??";
let roomId: string;
async function setupRoomWithBobAliceAndCharlies(page: Page, app: ElementAppPage, bob: Bot, charlies: Bot[]) {
async function setupRoomWithBobAliceAndCharlies(
page: Page,
app: ElementAppPage,
user: Credentials,
bob: Bot,
charlies: Bot[],
) {
const alias = `#lltest:${user.homeServer}`;
const visibility = await page.evaluate(() => (window as any).matrixcs.Visibility.Public);
roomId = await bob.createRoom({
name,
@@ -77,7 +88,7 @@ test.describe("Lazy Loading", () => {
}
function getMemberInMemberlist(page: Page, name: string): Locator {
return page.locator(".mx_MemberList .mx_EntityTile_name").filter({ hasText: name });
return page.locator(".mx_MemberListView .mx_MemberTileView_name").filter({ hasText: name });
}
async function checkMemberList(page: Page, charlies: Bot[]) {
@@ -94,7 +105,13 @@ test.describe("Lazy Loading", () => {
}
}
async function joinCharliesWhileAliceIsOffline(page: Page, app: ElementAppPage, charlies: Bot[]) {
async function joinCharliesWhileAliceIsOffline(
page: Page,
app: ElementAppPage,
user: Credentials,
charlies: Bot[],
) {
const alias = `#lltest:${user.homeServer}`;
await app.client.network.goOffline();
for (const charly of charlies) {
await charly.joinRoom(alias);
@@ -106,19 +123,19 @@ test.describe("Lazy Loading", () => {
await app.client.waitForNextSync();
}
test("should handle lazy loading properly even when offline", async ({ page, app, bot }) => {
test("should handle lazy loading properly even when offline", async ({ page, app, bot, user }) => {
test.slow();
const charly1to5 = charlies.slice(0, 5);
const charly6to10 = charlies.slice(5);
// Set up room with alice, bob & charlies 1-5
await setupRoomWithBobAliceAndCharlies(page, app, bot, charly1to5);
await setupRoomWithBobAliceAndCharlies(page, app, user, bot, charly1to5);
// Alice should see 2 messages from every charly with the correct display name
await checkPaginatedDisplayNames(app, charly1to5);
await openMemberlist(app);
await checkMemberList(page, charly1to5);
await joinCharliesWhileAliceIsOffline(page, app, charly6to10);
await joinCharliesWhileAliceIsOffline(page, app, user, charly6to10);
await checkMemberList(page, charly6to10);
for (const charly of charlies) {

View File

@@ -2,7 +2,7 @@
Copyright 2024 New Vector Ltd.
Copyright 2023 Suguru Hirahara
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/

View File

@@ -2,7 +2,7 @@
Copyright 2024 New Vector Ltd.
Copyright 2022 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
@@ -10,7 +10,8 @@ import { Locator, Page } from "@playwright/test";
import { test, expect } from "../../element-web-test";
test.describe("Location sharing", () => {
// Firefox headless lacks WebGL support https://bugzilla.mozilla.org/show_bug.cgi?id=1375585
test.describe("Location sharing", { tag: "@no-firefox" }, () => {
const selectLocationShareTypeOption = (page: Page, shareType: string): Locator => {
return page.getByTestId(`share-location-option-${shareType}`);
};

View File

@@ -2,18 +2,19 @@
Copyright 2024 New Vector Ltd.
Copyright 2022 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { test, expect } from "../../element-web-test";
import { consentHomeserver } from "../../plugins/homeserver/synapse/consentHomeserver.ts";
test.use(consentHomeserver);
test.use({
displayName: "Bob",
});
test.describe("Consent", () => {
test.use({
startHomeserverOpts: "consent",
displayName: "Bob",
});
test("should prompt the user to consent to terms when server deems it necessary", async ({
context,
page,

View File

@@ -2,18 +2,19 @@
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { Page } from "playwright-core";
import { expect, test } from "../../element-web-test";
import { doTokenRegistration } from "./utils";
import { isDendrite } from "../../plugins/homeserver/dendrite";
import { selectHomeserver } from "../utils";
import { Credentials, HomeserverInstance } from "../../plugins/homeserver";
import { consentHomeserver } from "../../plugins/homeserver/synapse/consentHomeserver.ts";
import { isDendrite } from "../../plugins/homeserver/dendrite";
// This test requires fixed credentials for the device signing keys below to work
const username = "user1234";
const password = "p4s5W0rD";
@@ -68,26 +69,55 @@ const DEVICE_SIGNING_KEYS_BODY = {
},
};
async function login(page: Page, homeserver: HomeserverInstance) {
async function login(page: Page, homeserver: HomeserverInstance, credentials: Credentials) {
await page.getByRole("link", { name: "Sign in" }).click();
await selectHomeserver(page, homeserver.config.baseUrl);
await selectHomeserver(page, homeserver.baseUrl);
await page.getByRole("textbox", { name: "Username" }).fill(username);
await page.getByPlaceholder("Password").fill(password);
await page.getByRole("textbox", { name: "Username" }).fill(credentials.username);
await page.getByPlaceholder("Password").fill(credentials.password);
await page.getByRole("button", { name: "Sign in" }).click();
}
test.describe("Login", () => {
test.describe("Password login", () => {
test.use({ startHomeserverOpts: "consent" });
// This test suite uses the same userId for all tests in the suite
// due to DEVICE_SIGNING_KEYS_BODY being specific to that userId,
// so we restart the Synapse container to make it forget everything.
test.use(consentHomeserver);
test.use({
config: {
// The only thing that we really *need* (otherwise Element refuses to load) is a default homeserver.
// We point that to a guaranteed-invalid domain.
default_server_config: {
"m.homeserver": {
base_url: "https://server.invalid",
},
},
},
context: async ({ context, homeserver }, use) => {
// Restart the homeserver to wipe its in-memory db so we can reuse the same user ID without cross-signing prompts
await homeserver.restart();
await use(context);
},
credentials: async ({ context, homeserver }, use) => {
const displayName = "Dave";
const credentials = await homeserver.registerUser(username, password, displayName);
console.log(`Registered test user @user:localhost with displayname ${displayName}`);
let creds: Credentials;
test.beforeEach(async ({ homeserver }) => {
creds = await homeserver.registerUser(username, password);
await use({
...credentials,
displayName,
});
// Restart the homeserver to wipe its in-memory db so we can reuse the same user ID without cross-signing prompts
await homeserver.restart();
},
});
test.describe("Login", () => {
test.describe("Password login", () => {
test.skip(isDendrite, "Dendrite lacks support for MSC3967 so requires additional auth here");
test("Loads the welcome page by default; then logs in with an existing account and lands on the home screen", async ({
credentials,
page,
homeserver,
checkA11y,
@@ -101,7 +131,7 @@ test.describe("Login", () => {
await page.getByRole("link", { name: "Sign in" }).click();
// first pick the homeserver, as otherwise the user picker won't be visible
await selectHomeserver(page, homeserver.config.baseUrl);
await selectHomeserver(page, homeserver.baseUrl);
await page.getByRole("button", { name: "Edit" }).click();
@@ -114,23 +144,23 @@ test.describe("Login", () => {
await expect(page.locator(".mx_ServerPicker_server")).toHaveText("server.invalid");
// switch back to the custom homeserver
await selectHomeserver(page, homeserver.config.baseUrl);
await selectHomeserver(page, homeserver.baseUrl);
await expect(page.getByRole("textbox", { name: "Username" })).toBeVisible();
// Disabled because flaky - see https://github.com/vector-im/element-web/issues/24688
// cy.percySnapshot("Login");
await checkA11y();
await page.getByRole("textbox", { name: "Username" }).fill(username);
await page.getByPlaceholder("Password").fill(password);
await page.getByRole("textbox", { name: "Username" }).fill(credentials.username);
await page.getByPlaceholder("Password").fill(credentials.password);
await page.getByRole("button", { name: "Sign in" }).click();
await expect(page).toHaveURL(/\/#\/home$/);
});
test("Follows the original link after login", async ({ page, homeserver }) => {
test("Follows the original link after login", async ({ page, homeserver, credentials }) => {
await page.goto("/#/room/!room:id"); // should redirect to the welcome page
await login(page, homeserver);
await login(page, homeserver, credentials);
await expect(page).toHaveURL(/\/#\/room\/!room:id$/);
await expect(page.getByRole("button", { name: "Join the discussion" })).toBeVisible();
@@ -141,18 +171,19 @@ test.describe("Login", () => {
page,
homeserver,
request,
credentials,
}) => {
const res = await request.post(
`${homeserver.config.baseUrl}/_matrix/client/v3/keys/device_signing/upload`,
{ headers: { Authorization: `Bearer ${creds.accessToken}` }, data: DEVICE_SIGNING_KEYS_BODY },
);
const res = await request.post(`${homeserver.baseUrl}/_matrix/client/v3/keys/device_signing/upload`, {
headers: { Authorization: `Bearer ${credentials.accessToken}` },
data: DEVICE_SIGNING_KEYS_BODY,
});
if (res.status() / 100 !== 2) {
console.log("Uploading dummy keys failed", await res.json());
}
expect(res.status() / 100).toEqual(2);
await page.goto("/");
await login(page, homeserver);
await login(page, homeserver, credentials);
await expect(page.getByRole("heading", { name: "Verify this device", level: 1 })).toBeVisible();
@@ -170,10 +201,14 @@ test.describe("Login", () => {
page,
homeserver,
request,
credentials,
}) => {
const res = await request.post(
`${homeserver.config.baseUrl}/_matrix/client/v3/keys/device_signing/upload`,
{ headers: { Authorization: `Bearer ${creds.accessToken}` }, data: DEVICE_SIGNING_KEYS_BODY },
`${homeserver.baseUrl}/_matrix/client/v3/keys/device_signing/upload`,
{
headers: { Authorization: `Bearer ${credentials.accessToken}` },
data: DEVICE_SIGNING_KEYS_BODY,
},
);
if (res.status() / 100 !== 2) {
console.log("Uploading dummy keys failed", await res.json());
@@ -181,7 +216,7 @@ test.describe("Login", () => {
expect(res.status() / 100).toEqual(2);
await page.goto("/");
await login(page, homeserver);
await login(page, homeserver, credentials);
await expect(page.getByRole("heading", { name: "Verify this device", level: 1 })).toBeVisible();
@@ -200,11 +235,15 @@ test.describe("Login", () => {
page,
homeserver,
request,
credentials,
}) => {
console.log(`uid ${creds.userId} body`, DEVICE_SIGNING_KEYS_BODY);
console.log(`uid ${credentials.userId} body`, DEVICE_SIGNING_KEYS_BODY);
const res = await request.post(
`${homeserver.config.baseUrl}/_matrix/client/v3/keys/device_signing/upload`,
{ headers: { Authorization: `Bearer ${creds.accessToken}` }, data: DEVICE_SIGNING_KEYS_BODY },
`${homeserver.baseUrl}/_matrix/client/v3/keys/device_signing/upload`,
{
headers: { Authorization: `Bearer ${credentials.accessToken}` },
data: DEVICE_SIGNING_KEYS_BODY,
},
);
if (res.status() / 100 !== 2) {
console.log("Uploading dummy keys failed", await res.json());
@@ -212,9 +251,9 @@ test.describe("Login", () => {
expect(res.status() / 100).toEqual(2);
await page.goto("/");
await login(page, homeserver);
await login(page, homeserver, credentials);
const h1 = await page.getByRole("heading", { name: "Verify this device", level: 1 });
const h1 = page.getByRole("heading", { name: "Verify this device", level: 1 });
await expect(h1).toBeVisible();
await expect(h1.locator(".mx_CompleteSecurity_skip")).toHaveCount(0);
@@ -223,32 +262,7 @@ test.describe("Login", () => {
});
});
// tests for old-style SSO login, in which we exchange tokens with Synapse, and Synapse talks to an auth server
test.describe("SSO login", () => {
test.skip(isDendrite, "does not yet support SSO");
test.use({
startHomeserverOpts: ({ oAuthServer }, use) =>
use({
template: "default",
oAuthServerPort: oAuthServer.port,
}),
});
test("logs in with SSO and lands on the home screen", async ({ page, homeserver }) => {
// If this test fails with a screen showing "Timeout connecting to remote server", it is most likely due to
// your firewall settings: Synapse is unable to reach the OIDC server.
//
// If you are using ufw, try something like:
// sudo ufw allow in on docker0
//
await doTokenRegistration(page, homeserver);
});
});
test.describe("logout", () => {
test.use({ startHomeserverOpts: "consent" });
test("should go to login page on logout", async ({ page, user }) => {
await page.getByRole("button", { name: "User menu" }).click();
await expect(page.getByText(user.displayName, { exact: true })).toBeVisible();
@@ -260,29 +274,4 @@ test.describe("Login", () => {
await expect(page).toHaveURL(/\/#\/login$/);
});
});
test.describe("logout with logout_redirect_url", () => {
test.use({
startHomeserverOpts: "consent",
config: {
// We redirect to decoder-ring because it's a predictable page that isn't Element itself.
// We could use example.org, matrix.org, or something else, however this puts dependency of external
// infrastructure on our tests. In the same vein, we don't really want to figure out how to ship a
// `test-landing.html` page when running with an uncontrolled Element (via `yarn start`).
// Using the decoder-ring is just as fine, and we can search for strategic names.
logout_redirect_url: "/decoder-ring/",
},
});
test("should respect logout_redirect_url", async ({ page, user }) => {
await page.getByRole("button", { name: "User menu" }).click();
await expect(page.getByText(user.displayName, { exact: true })).toBeVisible();
// give a change for the outstanding requests queue to settle before logging out
await page.waitForTimeout(2000);
await page.locator(".mx_UserMenu_contextMenu").getByRole("menuitem", { name: "Sign out" }).click();
await expect(page).toHaveURL(/\/decoder-ring\/$/);
});
});
});

View File

@@ -0,0 +1,29 @@
/*
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import { test } from "../../element-web-test";
import { doTokenRegistration } from "./utils";
import { isDendrite } from "../../plugins/homeserver/dendrite";
import { legacyOAuthHomeserver } from "../../plugins/homeserver/synapse/legacyOAuthHomeserver.ts";
test.use(legacyOAuthHomeserver);
// tests for old-style SSO login, in which we exchange tokens with Synapse, and Synapse talks to an auth server
test.describe("SSO login", () => {
test.skip(isDendrite, "does not yet support SSO");
test("logs in with SSO and lands on the home screen", async ({ page, homeserver }, testInfo) => {
// If this test fails with a screen showing "Timeout connecting to remote server", it is most likely due to
// your firewall settings: Synapse is unable to reach the OIDC server.
//
// If you are using ufw, try something like:
// sudo ufw allow in on docker0
//
await doTokenRegistration(page, homeserver, testInfo);
});
});

View File

@@ -0,0 +1,35 @@
/*
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import { expect, test } from "../../element-web-test";
import { consentHomeserver } from "../../plugins/homeserver/synapse/consentHomeserver.ts";
test.use(consentHomeserver);
test.use({
config: {
// We redirect to decoder-ring because it's a predictable page that isn't Element itself.
// We could use example.org, matrix.org, or something else, however this puts dependency of external
// infrastructure on our tests. In the same vein, we don't really want to figure out how to ship a
// `test-landing.html` page when running with an uncontrolled Element (via `yarn start`).
// Using the decoder-ring is just as fine, and we can search for strategic names.
logout_redirect_url: "/decoder-ring/",
},
});
test.describe("logout with logout_redirect_url", () => {
test("should respect logout_redirect_url", async ({ page, user }) => {
await page.getByRole("button", { name: "User menu" }).click();
await expect(page.getByText(user.displayName, { exact: true })).toBeVisible();
// give a change for the outstanding requests queue to settle before logging out
await page.waitForTimeout(2000);
await page.locator(".mx_UserMenu_contextMenu").getByRole("menuitem", { name: "Sign out" }).click();
await expect(page).toHaveURL(/\/decoder-ring\/$/);
});
});

View File

@@ -2,7 +2,7 @@
Copyright 2024 New Vector Ltd.
Copyright 2024 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
@@ -13,7 +13,7 @@ test.describe("Overwrite login action", () => {
// This seems terminally flakey: https://github.com/element-hq/element-web/issues/27363
// I tried verious things to try & deflake it, to no avail: https://github.com/matrix-org/matrix-react-sdk/pull/12506
test.skip("Try replace existing login with new one", async ({ page, app, credentials, homeserver }) => {
await logIntoElement(page, homeserver, credentials);
await logIntoElement(page, credentials);
const userMenu = await app.openUserMenu();
await expect(userMenu.getByText(credentials.userId)).toBeVisible();
@@ -24,7 +24,7 @@ test.describe("Overwrite login action", () => {
expect(credentials.userId).not.toBe(bobRegister.userId);
const clientCredentials /* IMatrixClientCreds */ = {
homeserverUrl: homeserver.config.baseUrl,
homeserverUrl: homeserver.baseUrl,
...bobRegister,
};

View File

@@ -2,121 +2,42 @@
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { Page } from "@playwright/test";
import { test, expect } from "../../element-web-test";
import { doTokenRegistration } from "./utils";
import { Credentials } from "../../plugins/homeserver";
import { isDendrite } from "../../plugins/homeserver/dendrite";
import { interceptRequestsWithSoftLogout } from "./utils";
test.describe("Soft logout", () => {
test.use({
displayName: "Alice",
startHomeserverOpts: ({ oAuthServer }, use) =>
use({
template: "default",
oAuthServerPort: oAuthServer.port,
}),
});
test.describe("with password user", () => {
test("shows the soft-logout page when a request fails, and allows a re-login", async ({ page, user }) => {
await interceptRequestsWithSoftLogout(page, user);
await expect(page.getByText("You're signed out")).toBeVisible();
await page.getByPlaceholder("Password").fill(user.password);
await page.getByPlaceholder("Password").press("Enter");
// back to the welcome page
await expect(page).toHaveURL(/\/#\/home/);
await expect(page.getByRole("heading", { name: `Welcome ${user.userId}`, exact: true })).toBeVisible();
});
test("still shows the soft-logout page when the page is reloaded after a soft-logout", async ({
page,
user,
}) => {
await interceptRequestsWithSoftLogout(page, user);
await expect(page.getByText("You're signed out")).toBeVisible();
await page.reload();
await expect(page.getByText("You're signed out")).toBeVisible();
});
});
test.describe("with SSO user", () => {
test.skip(isDendrite, "does not yet support SSO");
test.use({
user: async ({ page, homeserver }, use) => {
const user = await doTokenRegistration(page, homeserver);
// Eventually, we should end up at the home screen.
await expect(page).toHaveURL(/\/#\/home$/);
await expect(page.getByRole("heading", { name: "Welcome Alice", exact: true })).toBeVisible();
await use(user);
test.use({
displayName: "Alice",
config: {
// The only thing that we really *need* (otherwise Element refuses to load) is a default homeserver.
// We point that to a guaranteed-invalid domain.
default_server_config: {
"m.homeserver": {
base_url: "https://server.invalid",
},
});
test("shows the soft-logout page when a request fails, and allows a re-login", async ({ page, user }) => {
await expect(page.getByRole("heading", { name: "Welcome Alice", exact: true })).toBeVisible();
await interceptRequestsWithSoftLogout(page, user);
await expect(page.getByText("You're signed out")).toBeVisible();
await page.getByRole("button", { name: "Continue with OAuth test" }).click();
// click the submit button
await page.getByRole("button", { name: "Submit" }).click();
// Synapse prompts us to grant permission to Element
await expect(page.getByRole("heading", { name: "Continue to your account" })).toBeVisible();
await page.getByRole("link", { name: "Continue" }).click();
// back to the welcome page
await expect(page).toHaveURL(/\/#\/home$/);
await expect(page.getByRole("heading", { name: "Welcome Alice", exact: true })).toBeVisible();
});
});
},
},
});
/**
* Intercept calls to /sync and have them fail with a soft-logout
*
* Any further requests to /sync with the same access token are blocked.
*/
async function interceptRequestsWithSoftLogout(page: Page, user: Credentials): Promise<void> {
await page.route("**/_matrix/client/*/sync*", async (route, req) => {
const accessToken = await req.headerValue("Authorization");
test.describe("Soft logout with password user", () => {
test("shows the soft-logout page when a request fails, and allows a re-login", async ({ page, user }) => {
await interceptRequestsWithSoftLogout(page, user);
await expect(page.getByText("You're signed out")).toBeVisible();
await page.getByPlaceholder("Password").fill(user.password);
await page.getByPlaceholder("Password").press("Enter");
// now, if the access token on this request matches the expired one, block it
if (accessToken === `Bearer ${user.accessToken}`) {
console.log("Intercepting request with soft-logged-out access token");
await route.fulfill({
status: 401,
json: {
errcode: "M_UNKNOWN_TOKEN",
error: "Soft logout",
soft_logout: true,
},
});
return;
}
// otherwise, pass through as normal
await route.continue();
// back to the welcome page
await expect(page).toHaveURL(/\/#\/home/);
await expect(page.getByRole("heading", { name: "Now, let's help you get started", exact: true })).toBeVisible();
});
const promise = page.waitForResponse((resp) => resp.url().includes("/sync") && resp.status() === 401);
// do something to make the active /sync return: create a new room
await page.evaluate(() => {
// don't wait for this to complete: it probably won't, because of the broken sync
window.mxMatrixClientPeg.get().createRoom({});
test("still shows the soft-logout page when the page is reloaded after a soft-logout", async ({ page, user }) => {
await interceptRequestsWithSoftLogout(page, user);
await expect(page.getByText("You're signed out")).toBeVisible();
await page.reload();
await expect(page.getByText("You're signed out")).toBeVisible();
});
await promise;
}
});

View File

@@ -0,0 +1,59 @@
/*
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { test, expect } from "../../element-web-test";
import { doTokenRegistration, interceptRequestsWithSoftLogout } from "./utils";
import { legacyOAuthHomeserver } from "../../plugins/homeserver/synapse/legacyOAuthHomeserver.ts";
test.use({
displayName: "Alice",
config: {
// The only thing that we really *need* (otherwise Element refuses to load) is a default homeserver.
// We point that to a guaranteed-invalid domain.
default_server_config: {
"m.homeserver": {
base_url: "https://server.invalid",
},
},
},
});
test.use(legacyOAuthHomeserver);
test.describe("Soft logout with SSO user", () => {
test.use({
user: async ({ page, homeserver }, use, testInfo) => {
const user = await doTokenRegistration(page, homeserver, testInfo);
// Eventually, we should end up at the home screen.
await expect(page).toHaveURL(/\/#\/home$/);
await expect(page.getByRole("heading", { name: "Welcome Alice", exact: true })).toBeVisible();
await use(user);
},
});
test("shows the soft-logout page when a request fails, and allows a re-login", async ({ page, user }) => {
await expect(page.getByRole("heading", { name: "Welcome Alice", exact: true })).toBeVisible();
await interceptRequestsWithSoftLogout(page, user);
await expect(page.getByText("You're signed out")).toBeVisible();
await page.getByRole("button", { name: "Continue with OAuth test" }).click();
// click the submit button
await page.getByRole("button", { name: "Submit" }).click();
// Synapse prompts us to grant permission to Element
await expect(page.getByRole("heading", { name: "Continue to your account" })).toBeVisible();
await page.getByRole("link", { name: "Continue" }).click();
// back to the welcome page
await expect(page).toHaveURL(/\/#\/home$/);
await expect(page.getByRole("heading", { name: "Welcome Alice", exact: true })).toBeVisible();
});
});

View File

@@ -2,11 +2,11 @@
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { Page, expect } from "@playwright/test";
import { Page, expect, TestInfo } from "@playwright/test";
import { Credentials, HomeserverInstance } from "../../plugins/homeserver";
@@ -15,12 +15,13 @@ import { Credentials, HomeserverInstance } from "../../plugins/homeserver";
export async function doTokenRegistration(
page: Page,
homeserver: HomeserverInstance,
testInfo: TestInfo,
): Promise<Credentials & { displayName: string }> {
await page.goto("/#/login");
await page.getByRole("button", { name: "Edit" }).click();
await page.getByRole("textbox", { name: "Other homeserver" }).fill(homeserver.config.baseUrl);
await page.getByRole("button", { name: "Continue" }).click();
await page.getByRole("textbox", { name: "Other homeserver" }).fill(homeserver.baseUrl);
await page.getByRole("button", { name: "Continue", exact: true }).click();
// wait for the dialog to go away
await expect(page.locator(".mx_ServerPickerDialog")).toHaveCount(0);
@@ -35,7 +36,7 @@ export async function doTokenRegistration(
// Synapse prompts us to pick a user ID
await expect(page.getByRole("heading", { name: "Create your account" })).toBeVisible();
await page.getByRole("textbox", { name: "Username (required)" }).fill("alice");
await page.getByRole("textbox", { name: "Username (required)" }).fill(`alice_${testInfo.testId}`);
// wait for username validation to start, and complete
await expect(page.locator("#field-username-output")).toHaveText("");
@@ -56,5 +57,44 @@ export async function doTokenRegistration(
homeServer: window.mxMatrixClientPeg.get().getHomeserverUrl(),
password: null,
displayName: "Alice",
username: window.mxMatrixClientPeg.get().getUserIdLocalpart(),
}));
}
/**
* Intercept calls to /sync and have them fail with a soft-logout
*
* Any further requests to /sync with the same access token are blocked.
*/
export async function interceptRequestsWithSoftLogout(page: Page, user: Credentials): Promise<void> {
await page.route("**/_matrix/client/*/sync*", async (route, req) => {
const accessToken = await req.headerValue("Authorization");
// now, if the access token on this request matches the expired one, block it
if (accessToken === `Bearer ${user.accessToken}`) {
console.log("Intercepting request with soft-logged-out access token");
await route.fulfill({
status: 401,
json: {
errcode: "M_UNKNOWN_TOKEN",
error: "Soft logout",
soft_logout: true,
},
});
return;
}
// otherwise, pass through as normal
await route.continue();
});
const promise = page.waitForResponse((resp) => resp.url().includes("/sync") && resp.status() === 401);
// do something to make the active /sync return: create a new room
await page.evaluate(() => {
// don't wait for this to complete: it probably won't, because of the broken sync
void window.mxMatrixClientPeg.get().createRoom({});
});
await promise;
}

View File

@@ -2,7 +2,7 @@
Copyright 2024 New Vector Ltd.
Copyright 2024 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
@@ -58,6 +58,16 @@ async function editMessage(page: Page, message: Locator, newMsg: string): Promis
await editComposer.press("Enter");
}
const screenshotOptions = (page?: Page) => ({
mask: page ? [page.locator(".mx_MessageTimestamp")] : undefined,
// Hide the jump to bottom button in the timeline to avoid flakiness
css: `
.mx_JumpToBottomButton {
display: none !important;
}
`,
});
test.describe("Message rendering", () => {
[
{ direction: "ltr", displayName: "Quentin" },
@@ -79,9 +89,10 @@ test.describe("Message rendering", () => {
await page.goto(`#/room/${room.roomId}`);
const msgTile = await sendMessage(page, "Hello, world!");
await expect(msgTile).toMatchScreenshot(`basic-message-ltr-${direction}displayname.png`, {
mask: [page.locator(".mx_MessageTimestamp")],
});
await expect(msgTile).toMatchScreenshot(
`basic-message-ltr-${direction}displayname.png`,
screenshotOptions(page),
);
},
);
@@ -89,14 +100,17 @@ test.describe("Message rendering", () => {
await page.goto(`#/room/${room.roomId}`);
const msgTile = await sendMessage(page, "/me lays an egg");
await expect(msgTile).toMatchScreenshot(`emote-ltr-${direction}displayname.png`);
await expect(msgTile).toMatchScreenshot(`emote-ltr-${direction}displayname.png`, screenshotOptions());
});
test("should render an LTR rich text emote", async ({ page, user, app, room }) => {
await page.goto(`#/room/${room.roomId}`);
const msgTile = await sendMessage(page, "/me lays a *free range* egg");
await expect(msgTile).toMatchScreenshot(`emote-rich-ltr-${direction}displayname.png`);
await expect(msgTile).toMatchScreenshot(
`emote-rich-ltr-${direction}displayname.png`,
screenshotOptions(),
);
});
test("should render an edited LTR message", async ({ page, user, app, room }) => {
@@ -106,9 +120,10 @@ test.describe("Message rendering", () => {
await editMessage(page, msgTile, "Hello, universe!");
await expect(msgTile).toMatchScreenshot(`edited-message-ltr-${direction}displayname.png`, {
mask: [page.locator(".mx_MessageTimestamp")],
});
await expect(msgTile).toMatchScreenshot(
`edited-message-ltr-${direction}displayname.png`,
screenshotOptions(page),
);
});
test("should render a reply of a LTR message", async ({ page, user, app, room }) => {
@@ -122,32 +137,37 @@ test.describe("Message rendering", () => {
]);
await replyMessage(page, msgTile, "response to multiline message");
await expect(msgTile).toMatchScreenshot(`reply-message-ltr-${direction}displayname.png`, {
mask: [page.locator(".mx_MessageTimestamp")],
});
await expect(msgTile).toMatchScreenshot(
`reply-message-ltr-${direction}displayname.png`,
screenshotOptions(page),
);
});
test("should render a basic RTL text message", async ({ page, user, app, room }) => {
await page.goto(`#/room/${room.roomId}`);
const msgTile = await sendMessage(page, "مرحبا بالعالم!");
await expect(msgTile).toMatchScreenshot(`basic-message-rtl-${direction}displayname.png`, {
mask: [page.locator(".mx_MessageTimestamp")],
});
await expect(msgTile).toMatchScreenshot(
`basic-message-rtl-${direction}displayname.png`,
screenshotOptions(page),
);
});
test("should render an RTL emote", async ({ page, user, app, room }) => {
await page.goto(`#/room/${room.roomId}`);
const msgTile = await sendMessage(page, "/me يضع بيضة");
await expect(msgTile).toMatchScreenshot(`emote-rtl-${direction}displayname.png`);
await expect(msgTile).toMatchScreenshot(`emote-rtl-${direction}displayname.png`, screenshotOptions());
});
test("should render a richtext RTL emote", async ({ page, user, app, room }) => {
await page.goto(`#/room/${room.roomId}`);
const msgTile = await sendMessage(page, "/me أضع بيضة *حرة النطاق*");
await expect(msgTile).toMatchScreenshot(`emote-rich-rtl-${direction}displayname.png`);
await expect(msgTile).toMatchScreenshot(
`emote-rich-rtl-${direction}displayname.png`,
screenshotOptions(),
);
});
test("should render an edited RTL message", async ({ page, user, app, room }) => {
@@ -157,9 +177,10 @@ test.describe("Message rendering", () => {
await editMessage(page, msgTile, "مرحبا بالكون!");
await expect(msgTile).toMatchScreenshot(`edited-message-rtl-${direction}displayname.png`, {
mask: [page.locator(".mx_MessageTimestamp")],
});
await expect(msgTile).toMatchScreenshot(
`edited-message-rtl-${direction}displayname.png`,
screenshotOptions(page),
);
});
test("should render a reply of a RTL message", async ({ page, user, app, room }) => {
@@ -173,9 +194,10 @@ test.describe("Message rendering", () => {
]);
await replyMessage(page, msgTile, "مرحبا بالعالم!");
await expect(msgTile).toMatchScreenshot(`reply-message-trl-${direction}displayname.png`, {
mask: [page.locator(".mx_MessageTimestamp")],
});
await expect(msgTile).toMatchScreenshot(
`reply-message-trl-${direction}displayname.png`,
screenshotOptions(page),
);
});
});
});

View File

@@ -2,72 +2,18 @@
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { API, Messages } from "mailhog";
import { MailpitClient } from "mailpit-api";
import { Page } from "@playwright/test";
import { test as base, expect } from "../../element-web-test";
import { MatrixAuthenticationService } from "../../plugins/matrix-authentication-service";
import { StartHomeserverOpts } from "../../plugins/homeserver";
export const test = base.extend<{
masPrepare: MatrixAuthenticationService;
mas: MatrixAuthenticationService;
}>({
// There's a bit of a chicken and egg problem between MAS & Synapse where they each need to know how to reach each other
// so spinning up a MAS is split into the prepare & start stage: prepare mas -> homeserver -> start mas to disentangle this.
masPrepare: async ({ context }, use) => {
const mas = new MatrixAuthenticationService(context);
await mas.prepare();
await use(mas);
},
mas: [
async ({ masPrepare: mas, homeserver, mailhog }, use, testInfo) => {
await mas.start(homeserver, mailhog.instance);
await use(mas);
await mas.stop(testInfo);
},
{ auto: true },
],
startHomeserverOpts: async ({ masPrepare }, use) => {
await use({
template: "mas-oidc",
variables: {
MAS_PORT: masPrepare.port,
},
});
},
config: async ({ homeserver, startHomeserverOpts, context }, use) => {
const issuer = `http://localhost:${(startHomeserverOpts as StartHomeserverOpts).variables["MAS_PORT"]}/`;
const wellKnown = {
"m.homeserver": {
base_url: homeserver.config.baseUrl,
},
"org.matrix.msc2965.authentication": {
issuer,
account: `${issuer}account`,
},
};
// Ensure org.matrix.msc2965.authentication is in well-known
await context.route("https://localhost/.well-known/matrix/client", async (route) => {
await route.fulfill({ json: wellKnown });
});
await use({
default_server_config: wellKnown,
});
},
});
export { expect };
import { expect } from "../../element-web-test";
export async function registerAccountMas(
page: Page,
mailhog: API,
mailpit: MailpitClient,
username: string,
email: string,
password: string,
@@ -81,13 +27,13 @@ export async function registerAccountMas(
await page.getByRole("textbox", { name: "Confirm Password" }).fill(password);
await page.getByRole("button", { name: "Continue" }).click();
let messages: Messages;
let code: string;
await expect(async () => {
messages = await mailhog.messages();
expect(messages.items).toHaveLength(1);
const messages = await mailpit.listMessages();
expect(messages.messages[0].To[0].Address).toEqual(email);
const text = await mailpit.renderMessageText(messages.messages[0].ID);
[, code] = text.match(/Your verification code to confirm this email address is: (\d{6})/);
}).toPass();
expect(messages.items[0].to).toEqual(`${username} <${email}>`);
const [code] = messages.items[0].text.match(/(\d{6})/);
await page.getByRole("textbox", { name: "6-digit code" }).fill(code);
await page.getByRole("button", { name: "Continue" }).click();

View File

@@ -1,34 +0,0 @@
/*
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import { test, expect, registerAccountMas } from ".";
import { isDendrite } from "../../plugins/homeserver/dendrite";
test.describe("OIDC Aware", () => {
test.skip(isDendrite, "does not yet support MAS");
test.slow(); // trace recording takes a while here
test("can register an account and manage it", async ({ context, page, homeserver, mailhog, app }) => {
await page.goto("/#/login");
await page.getByRole("button", { name: "Continue" }).click();
await registerAccountMas(page, mailhog.api, "alice", "alice@email.com", "Pa$sW0rD!");
// Eventually, we should end up at the home screen.
await expect(page).toHaveURL(/\/#\/home$/, { timeout: 10000 });
await expect(page.getByRole("heading", { name: "Welcome alice", exact: true })).toBeVisible();
// Open settings and navigate to account management
await app.settings.openUserSettings("Account");
const newPagePromise = context.waitForEvent("page");
await page.getByRole("button", { name: "Manage account" }).click();
// Assert new tab opened
const newPage = await newPagePromise;
await expect(newPage.getByText("Primary email")).toBeVisible();
});
});

View File

@@ -2,35 +2,43 @@
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { test, expect, registerAccountMas } from ".";
import { isDendrite } from "../../plugins/homeserver/dendrite";
import { test, expect } from "../../element-web-test.ts";
import { registerAccountMas } from ".";
import { ElementAppPage } from "../../pages/ElementAppPage.ts";
import { masHomeserver } from "../../plugins/homeserver/synapse/masHomeserver.ts";
test.describe("OIDC Native", () => {
test.skip(isDendrite, "does not yet support MAS");
test.use(masHomeserver);
test.describe("OIDC Native", { tag: ["@no-firefox", "@no-webkit"] }, () => {
test.slow(); // trace recording takes a while here
test.use({
labsFlags: ["feature_oidc_native_flow"],
});
test("can register the oauth2 client and an account", async ({
context,
page,
homeserver,
mailpitClient,
mas,
}, testInfo) => {
await page.clock.install();
test("can register the oauth2 client and an account", async ({ context, page, homeserver, mailhog, mas }) => {
const tokenUri = `http://localhost:${mas.port}/oauth2/token`;
const tokenUri = `${mas.baseUrl}/oauth2/token`;
const tokenApiPromise = page.waitForRequest(
(request) => request.url() === tokenUri && request.postDataJSON()["grant_type"] === "authorization_code",
);
await page.goto("/#/login");
await page.getByRole("button", { name: "Continue" }).click();
await registerAccountMas(page, mailhog.api, "alice", "alice@email.com", "Pa$sW0rD!");
const userId = `alice_${testInfo.testId}`;
await registerAccountMas(page, mailpitClient, userId, "alice@email.com", "Pa$sW0rD!");
// Eventually, we should end up at the home screen.
await expect(page).toHaveURL(/\/#\/home$/, { timeout: 10000 });
await expect(page.getByRole("heading", { name: "Welcome alice", exact: true })).toBeVisible();
await expect(page.getByRole("heading", { name: `Welcome ${userId}`, exact: true })).toBeVisible();
await page.clock.runFor(20000); // run the timer so we see the token request
const tokenApiRequest = await tokenApiPromise;
expect(tokenApiRequest.postDataJSON()["grant_type"]).toBe("authorization_code");
@@ -45,15 +53,15 @@ test.describe("OIDC Native", () => {
// Assert MAS sees the session as OIDC Native
const newPage = await newPagePromise;
await newPage.getByText("Sessions").click();
await newPage.getByText("Devices").click();
await newPage.getByText(deviceId).click();
await expect(newPage.getByText("Element")).toBeVisible();
await expect(newPage.getByText("oauth2_session:")).toBeVisible();
await expect(newPage.getByText("http://localhost:8080/")).toBeVisible();
await expect(newPage).toHaveURL(/\/oauth2_session/);
await newPage.close();
// Assert logging out revokes both tokens
const revokeUri = `http://localhost:${mas.port}/oauth2/revoke`;
const revokeUri = `${mas.baseUrl}/oauth2/revoke`;
const revokeAccessTokenPromise = page.waitForRequest(
(request) => request.url() === revokeUri && request.postDataJSON()["token_type_hint"] === "access_token",
);

View File

@@ -3,22 +3,25 @@ Copyright 2024 New Vector Ltd.
Copyright 2023 Ahmad Kadri
Copyright 2023 Nordeck IT + Consulting GmbH.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { test as base, expect } from "../../element-web-test";
import { Credentials } from "../../plugins/homeserver";
import { isDendrite } from "../../plugins/homeserver/dendrite";
const test = base.extend<{
user2?: Credentials;
}>({});
test.describe("1:1 chat room", () => {
test.skip(isDendrite, "due to a Dendrite bug https://github.com/element-hq/dendrite/issues/3492");
test.use({
displayName: "Jeff",
user2: async ({ homeserver }, use) => {
const credentials = await homeserver.registerUser("user1234", "p4s5W0rD", "Timmy");
user2: async ({ homeserver }, use, testInfo) => {
const credentials = await homeserver.registerUser(`user2_${testInfo.testId}`, "p4s5W0rD", "Timmy");
await use(credentials);
},
});

View File

@@ -2,7 +2,7 @@
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
@@ -31,7 +31,7 @@ test.describe("permalinks", () => {
await charlotte.prepareClient();
// We don't use a bot for danielle as we want a stable MXID.
const danielleId = "@danielle:localhost";
const danielleId = `@danielle:${user.homeServer}`;
const room1Id = await app.client.createRoom({ name: room1Name });
const room2Id = await app.client.createRoom({ name: room2Name });

View File

@@ -2,7 +2,7 @@
* Copyright 2024 New Vector Ltd.
* Copyright 2024 The Matrix.org Foundation C.I.C.
*
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
@@ -13,6 +13,8 @@ import { Client } from "../../pages/client";
import { ElementAppPage } from "../../pages/ElementAppPage";
import { Bot } from "../../pages/bot";
type RoomRef = { name: string; roomId: string };
/**
* Set up for pinned message tests.
*/
@@ -47,7 +49,7 @@ export class Helpers {
* @param room - the name of the room to send messages into
* @param messages - the list of messages to send, these can be strings or implementations of MessageSpec like `editOf`
*/
async receiveMessages(room: string | { name: string }, messages: string[]) {
async receiveMessages(room: RoomRef, messages: string[]) {
await this.sendMessageAsClient(this.bot, room, messages);
}
@@ -55,9 +57,8 @@ export class Helpers {
* Use the supplied client to send messages or perform actions as specified by
* the supplied {@link Message} items.
*/
private async sendMessageAsClient(cli: Client, roomName: string | { name: string }, messages: string[]) {
const room = await this.findRoomByName(typeof roomName === "string" ? roomName : roomName.name);
const roomId = await room.evaluate((room) => room.roomId);
private async sendMessageAsClient(cli: Client, room: RoomRef, messages: string[]) {
const roomId = room.roomId;
for (const message of messages) {
await cli.sendMessage(roomId, { body: message, msgtype: "m.text" });
@@ -73,22 +74,11 @@ export class Helpers {
}
}
/**
* Find a room by its name
* @param roomName
* @private
*/
private async findRoomByName(roomName: string) {
return this.app.client.evaluateHandle((cli, roomName) => {
return cli.getRooms().find((r) => r.name === roomName);
}, roomName);
}
/**
* Open the room with the supplied name.
*/
async goTo(room: string | { name: string }) {
await this.app.viewRoomByName(typeof room === "string" ? room : room.name);
async goTo(room: RoomRef) {
await this.app.viewRoomByName(room.name);
}
/**

View File

@@ -2,7 +2,7 @@
* Copyright 2024 New Vector Ltd.
* Copyright 2024 The Matrix.org Foundation C.I.C.
*
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
@@ -35,10 +35,10 @@ test.describe("Pinned messages", () => {
mask: [tile.locator(".mx_MessageTimestamp")],
// Hide the jump to bottom button in the timeline to avoid flakiness
css: `
.mx_JumpToBottomButton {
display: none !important;
}
`,
.mx_JumpToBottomButton {
display: none !important;
}
`,
});
},
);

View File

@@ -2,7 +2,7 @@
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { test, expect } from "../../element-web-test";
@@ -134,7 +134,7 @@ test.describe("Poll history", () => {
await expect(dialog.getByText(pollParams2.title)).toBeAttached();
await expect(dialog.getByText(pollParams1.title)).toBeAttached();
dialog.getByText("Active polls").click();
await dialog.getByText("Active polls").click();
// no more active polls
await expect(page.getByText("There are no active polls in this room")).toBeAttached();

View File

@@ -2,7 +2,7 @@
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
@@ -11,8 +11,11 @@ import { Bot } from "../../pages/bot";
import { SettingLevel } from "../../../src/settings/SettingLevel";
import { Layout } from "../../../src/settings/enums/Layout";
import type { Locator, Page } from "@playwright/test";
import { isDendrite } from "../../plugins/homeserver/dendrite";
test.describe("Polls", () => {
test.skip(isDendrite, "due to a Dendrite bug https://github.com/element-hq/dendrite/issues/3492");
type CreatePollOptions = {
title: string;
options: string[];

View File

@@ -2,7 +2,7 @@
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/

View File

@@ -2,15 +2,18 @@
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
/* See readme.md for tips on writing these tests. */
import { test } from ".";
import { isDendrite } from "../../plugins/homeserver/dendrite";
test.describe("Read receipts", { tag: "@mergequeue" }, () => {
test.skip(isDendrite, "due to Dendrite bug https://github.com/element-hq/dendrite/issues/2970");
test.describe("editing messages", () => {
test.describe("in threads", () => {
test("An edit of a threaded message makes the room unread", async ({

View File

@@ -2,15 +2,18 @@
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
/* See readme.md for tips on writing these tests. */
import { test } from ".";
import { isDendrite } from "../../plugins/homeserver/dendrite";
test.describe("Read receipts", { tag: "@mergequeue" }, () => {
test.skip(isDendrite, "due to Dendrite bug https://github.com/element-hq/dendrite/issues/2970");
test.describe("editing messages", () => {
test.describe("in the main timeline", () => {
test("Editing a message leaves a room read", async ({ roomAlpha: room1, roomBeta: room2, util, msg }) => {

View File

@@ -2,15 +2,18 @@
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
/* See readme.md for tips on writing these tests. */
import { test } from ".";
import { isDendrite } from "../../plugins/homeserver/dendrite";
test.describe("Read receipts", { tag: "@mergequeue" }, () => {
test.skip(isDendrite, "due to Dendrite bug https://github.com/element-hq/dendrite/issues/2970");
test.describe("editing messages", () => {
test.describe("thread roots", () => {
test("An edit of a thread root leaves the room read", async ({

View File

@@ -2,15 +2,19 @@
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
/* See readme.md for tips on writing these tests. */
import { customEvent, many, test } from ".";
import { isDendrite } from "../../plugins/homeserver/dendrite";
test.describe("Read receipts", { tag: "@mergequeue" }, () => {
test.skip(isDendrite, "due to Dendrite bug https://github.com/element-hq/dendrite/issues/2970");
test.slow();
test.describe("Ignored events", () => {
test("If all events after receipt are unimportant, the room is read", async ({
roomAlpha: room1,
@@ -120,7 +124,7 @@ test.describe("Read receipts", { tag: "@mergequeue" }, () => {
await util.assertUnread(room2, 40);
// When I jump to a message in the middle and page up
await msg.jumpTo(room2.name, "x\ny\nz\nMsg0020");
await msg.jumpTo(room2, "x\ny\nz\nMsg0020");
await util.pageUp();
// Then the room is still unread

Some files were not shown because too many files have changed in this diff Show More