mirror of
https://github.com/element-hq/element-web.git
synced 2025-12-13 01:50:46 +00:00
Compare commits
472 Commits
t3chguy/na
...
robin/reve
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63d32efb27 | ||
|
|
f05df80b46 | ||
|
|
9a109cdce8 | ||
|
|
a0ab88943b | ||
|
|
ad01218942 | ||
|
|
e1e4d26154 | ||
|
|
84c614676d | ||
|
|
29d9e98111 | ||
|
|
9f5f898ed8 | ||
|
|
78251a3a8a | ||
|
|
1b077c53f5 | ||
|
|
68828a2326 | ||
|
|
af8d93f58a | ||
|
|
c0a097867e | ||
|
|
0b13e57518 | ||
|
|
8615b411b2 | ||
|
|
3d31376b1d | ||
|
|
43e5124cd4 | ||
|
|
19674cca08 | ||
|
|
6ca6cb0fbe | ||
|
|
d92fc5a595 | ||
|
|
b9d411eecc | ||
|
|
3da6619bcf | ||
|
|
f33e7c9782 | ||
|
|
1ebae09834 | ||
|
|
56eafc908e | ||
|
|
1644169ff3 | ||
|
|
cf895b4296 | ||
|
|
e9d4f39e9d | ||
|
|
7c0ec21365 | ||
|
|
72df9c9076 | ||
|
|
e42ee727b4 | ||
|
|
7d30413178 | ||
|
|
8884e77ce3 | ||
|
|
58f812ffe6 | ||
|
|
ef1597ff2d | ||
|
|
e5ca7954c8 | ||
|
|
13913ba8b2 | ||
|
|
03a1b48e1f | ||
|
|
ef3bf59656 | ||
|
|
7a3202b537 | ||
|
|
dbce48b23d | ||
|
|
bb41616d5f | ||
|
|
c75f6dc3a1 | ||
|
|
880048d998 | ||
|
|
24685dc7d1 | ||
|
|
60f70b93e0 | ||
|
|
2559cba482 | ||
|
|
5882b004f5 | ||
|
|
37f8d70d89 | ||
|
|
e2bd040c88 | ||
|
|
381b2ea343 | ||
|
|
41944e5c6e | ||
|
|
540580504d | ||
|
|
1a21b718d8 | ||
|
|
2cddb16a9f | ||
|
|
671d6de805 | ||
|
|
0f8a2e93ce | ||
|
|
bff2d680e6 | ||
|
|
5a5db19c2c | ||
|
|
11a8723c73 | ||
|
|
e14a3b64c3 | ||
|
|
f99d7ce2bb | ||
|
|
585aa75525 | ||
|
|
effef7eaa7 | ||
|
|
9826a8851d | ||
|
|
ebef0d353e | ||
|
|
f1899b9eb1 | ||
|
|
027891a35a | ||
|
|
2f7c28ded0 | ||
|
|
b6aba1477b | ||
|
|
056ecbb138 | ||
|
|
7685e547de | ||
|
|
a0a4211447 | ||
|
|
0ad4e13e2d | ||
|
|
f406305510 | ||
|
|
6cb174e3d9 | ||
|
|
c569478240 | ||
|
|
2bd8e049c7 | ||
|
|
e8d69dc592 | ||
|
|
50ac509a01 | ||
|
|
3e27a0019d | ||
|
|
5caad70191 | ||
|
|
6846679d34 | ||
|
|
7e5420100a | ||
|
|
f75d1f5a5e | ||
|
|
66bbb84e56 | ||
|
|
48152d2cd1 | ||
|
|
0d50e34763 | ||
|
|
f157c90ba9 | ||
|
|
cccb847d4e | ||
|
|
a5b739c45a | ||
|
|
9b1e165e6c | ||
|
|
0e2b16abf1 | ||
|
|
f6e999c87d | ||
|
|
3983bd5646 | ||
|
|
e8402f1657 | ||
|
|
69237e7df2 | ||
|
|
cf1c0805f1 | ||
|
|
b6a1aea825 | ||
|
|
b97005c182 | ||
|
|
9599c57a20 | ||
|
|
3eb3b936d9 | ||
|
|
b488155910 | ||
|
|
afc0dd5f86 | ||
|
|
7acadc29cc | ||
|
|
a73faffe37 | ||
|
|
1e758cacae | ||
|
|
7b565e7997 | ||
|
|
b16088d098 | ||
|
|
29624f7bcb | ||
|
|
d8f6c12c3d | ||
|
|
69ee8fd96a | ||
|
|
3fb10baedf | ||
|
|
cf49f9e22c | ||
|
|
703149d76d | ||
|
|
bd3e93e8dd | ||
|
|
0555701829 | ||
|
|
417db4c9b2 | ||
|
|
4e151f8d03 | ||
|
|
afa7ec695d | ||
|
|
e98529824e | ||
|
|
16d2cccb73 | ||
|
|
9d5141cfaa | ||
|
|
1e42f28a69 | ||
|
|
4e1bd69e4d | ||
|
|
12943954c6 | ||
|
|
ec95435724 | ||
|
|
7e1927d388 | ||
|
|
0b24d33c64 | ||
|
|
db02f26005 | ||
|
|
b07d10cb23 | ||
|
|
179b17434e | ||
|
|
ab401160f8 | ||
|
|
5448de5dd6 | ||
|
|
be181d2c79 | ||
|
|
baaed75c4b | ||
|
|
cd7cf86b96 | ||
|
|
2c4a079153 | ||
|
|
9099338af8 | ||
|
|
f621c342ff | ||
|
|
4c1924311f | ||
|
|
a7e3764c27 | ||
|
|
07f1680ba0 | ||
|
|
3fbc9e6de6 | ||
|
|
117bee787f | ||
|
|
580213da5d | ||
|
|
22530d6ea5 | ||
|
|
e7d9df24e2 | ||
|
|
95c879c9e5 | ||
|
|
cbc1838755 | ||
|
|
c2799a1812 | ||
|
|
980b922348 | ||
|
|
ad77f7943b | ||
|
|
89d7dca464 | ||
|
|
aa44cadb02 | ||
|
|
941f4e1005 | ||
|
|
9b85c2d0fd | ||
|
|
1e0dfd0241 | ||
|
|
bea1b8eb85 | ||
|
|
d5db16ca24 | ||
|
|
edaf9773c0 | ||
|
|
7ea188cf89 | ||
|
|
a581e776a8 | ||
|
|
8d261d9819 | ||
|
|
299270e52d | ||
|
|
943b817194 | ||
|
|
2aa72bb40b | ||
|
|
a755e399cf | ||
|
|
8dff758153 | ||
|
|
cf3bdbdc7a | ||
|
|
ba98c2085d | ||
|
|
b330de5d6e | ||
|
|
b86bb5cc2f | ||
|
|
e835cab139 | ||
|
|
af3040fb62 | ||
|
|
b6ba3335ec | ||
|
|
6b7c94905f | ||
|
|
a4e8bb3f9a | ||
|
|
2b4000d47f | ||
|
|
01304439ee | ||
|
|
c659afa8db | ||
|
|
9cc5564d50 | ||
|
|
549300726f | ||
|
|
319dab5920 | ||
|
|
5c51d179b9 | ||
|
|
dbdb23f6bc | ||
|
|
5686666ad2 | ||
|
|
0c4189f2ed | ||
|
|
450cb608ec | ||
|
|
7e03f38a3b | ||
|
|
9bf3d22439 | ||
|
|
5547101bcc | ||
|
|
085854b125 | ||
|
|
ee24989f49 | ||
|
|
5a418f3f19 | ||
|
|
db5b3359c6 | ||
|
|
188f910dc7 | ||
|
|
619e41e3a2 | ||
|
|
c1838b34b6 | ||
|
|
1d51323451 | ||
|
|
d0d0b8212d | ||
|
|
974d3c175a | ||
|
|
d0e19d3e03 | ||
|
|
b016cf59e9 | ||
|
|
cfdfc4e640 | ||
|
|
d0fea745bb | ||
|
|
f3ef9e6602 | ||
|
|
af0391b86a | ||
|
|
36108c0c22 | ||
|
|
d2acce1221 | ||
|
|
b72c053d1a | ||
|
|
865c5b0e9c | ||
|
|
ce3fa2164f | ||
|
|
60b0e8b237 | ||
|
|
823828fc17 | ||
|
|
8774a68a13 | ||
|
|
4730352092 | ||
|
|
0429809c00 | ||
|
|
06fa3481df | ||
|
|
e75ff818d3 | ||
|
|
2c3e01a31c | ||
|
|
84709df3c9 | ||
|
|
00aadf1580 | ||
|
|
d8ebc68aa8 | ||
|
|
5d72735b1f | ||
|
|
2099aaa663 | ||
|
|
6d8cbf39f5 | ||
|
|
b87437d439 | ||
|
|
8619a22f57 | ||
|
|
418f121f96 | ||
|
|
351774d3e3 | ||
|
|
70f898c71d | ||
|
|
4f276c1690 | ||
|
|
2b4ce627b8 | ||
|
|
d68c5a26af | ||
|
|
95175caf0c | ||
|
|
08418c16c9 | ||
|
|
6798239aa8 | ||
|
|
9c74110969 | ||
|
|
1bee3becfb | ||
|
|
bfac727307 | ||
|
|
8e213c5d34 | ||
|
|
c34cbd011d | ||
|
|
e10ecc9a4d | ||
|
|
3dbcb5efa3 | ||
|
|
2b883a8aa0 | ||
|
|
bb54a0e063 | ||
|
|
af3a9777e8 | ||
|
|
61542ff3a8 | ||
|
|
ec460326f1 | ||
|
|
af846f8be9 | ||
|
|
de5ddcf6f7 | ||
|
|
8d28dd3784 | ||
|
|
df7703a4a5 | ||
|
|
fe7ac68478 | ||
|
|
bda045fad0 | ||
|
|
8a7cdaa3ef | ||
|
|
7796200562 | ||
|
|
80cd93678a | ||
|
|
b8c178d133 | ||
|
|
5f4d789259 | ||
|
|
85711be352 | ||
|
|
a891dcddc2 | ||
|
|
61947deef5 | ||
|
|
c1549e6aaa | ||
|
|
adfc66bd1b | ||
|
|
dbfb84eb77 | ||
|
|
3d48168394 | ||
|
|
9ddd5d96eb | ||
|
|
e6dc6b93a7 | ||
|
|
bd0bb879ec | ||
|
|
903928c33c | ||
|
|
0525887be4 | ||
|
|
4259e96c90 | ||
|
|
20532144d2 | ||
|
|
d2eeb3d8af | ||
|
|
b4445fed53 | ||
|
|
9860f9320a | ||
|
|
c8f46a0191 | ||
|
|
4285b4b140 | ||
|
|
de820e11fc | ||
|
|
94130be5a7 | ||
|
|
0333cba258 | ||
|
|
2ebc1b4a89 | ||
|
|
756ce2c639 | ||
|
|
c519361b4e | ||
|
|
15bd59b81a | ||
|
|
c74f9159ad | ||
|
|
2ac2bae4fa | ||
|
|
a478463b75 | ||
|
|
3f221891f7 | ||
|
|
a2a066d8b4 | ||
|
|
ede91bf921 | ||
|
|
fd7c50f61d | ||
|
|
3d6664109b | ||
|
|
7d20bd4d06 | ||
|
|
e0e522a0e9 | ||
|
|
8df26a54cf | ||
|
|
c2ce7dbc5e | ||
|
|
affa4e5211 | ||
|
|
04604e5171 | ||
|
|
0e38df84c8 | ||
|
|
2ce588ac64 | ||
|
|
cdfb58501d | ||
|
|
f89f050f07 | ||
|
|
eddaf7c47e | ||
|
|
3860226ee2 | ||
|
|
b907ec380f | ||
|
|
5d7c71210f | ||
|
|
d8844c682b | ||
|
|
72989ea646 | ||
|
|
95630f525f | ||
|
|
d5c111f656 | ||
|
|
7329a5f1fc | ||
|
|
2063624fb6 | ||
|
|
03a1d89785 | ||
|
|
5cdcf44b6f | ||
|
|
ca33d9165a | ||
|
|
2dffadc92e | ||
|
|
48fd330dd9 | ||
|
|
f0af77712f | ||
|
|
60ff33fecc | ||
|
|
3bcc27a444 | ||
|
|
7e33f03a02 | ||
|
|
0d5c9a338b | ||
|
|
d4ab40990b | ||
|
|
c8e4ffe1dd | ||
|
|
0ae74a9e1f | ||
|
|
ed9795137b | ||
|
|
4f8e9eb9ac | ||
|
|
08f41a48a8 | ||
|
|
9b316e8e7f | ||
|
|
72a2773629 | ||
|
|
abf6d58b7b | ||
|
|
cafa02ccc2 | ||
|
|
28640eec5f | ||
|
|
ce928e8d1f | ||
|
|
774b767b80 | ||
|
|
e7cd322559 | ||
|
|
ae3ca52bd2 | ||
|
|
d36cfc37e2 | ||
|
|
e3f8a7b13d | ||
|
|
048f88e10e | ||
|
|
9a126795a8 | ||
|
|
5b0aa511a6 | ||
|
|
346b45751c | ||
|
|
4f7c61ff68 | ||
|
|
da2e126ce1 | ||
|
|
18ef975386 | ||
|
|
ec96d33ed7 | ||
|
|
926c45488d | ||
|
|
b8e54750a0 | ||
|
|
84bd188dd7 | ||
|
|
ca239fee4d | ||
|
|
349c9b0c26 | ||
|
|
73db771ff3 | ||
|
|
f77d9b4bcb | ||
|
|
27b62d022e | ||
|
|
5c3a518576 | ||
|
|
3132fe3233 | ||
|
|
6e5f593c98 | ||
|
|
8bf3ec8376 | ||
|
|
eb938c21d6 | ||
|
|
e5c0cdc402 | ||
|
|
8a756b592c | ||
|
|
c67e67af4e | ||
|
|
7b1e303328 | ||
|
|
27a43e860a | ||
|
|
9b5d0866e0 | ||
|
|
86c6ba9dd7 | ||
|
|
0faf298e05 | ||
|
|
ef382584f0 | ||
|
|
d8d60170ac | ||
|
|
159d62f7d3 | ||
|
|
6424853f27 | ||
|
|
84b07cbbaa | ||
|
|
b03c84f51e | ||
|
|
ad2dda960e | ||
|
|
ee63710ef1 | ||
|
|
90f9f53bc1 | ||
|
|
3c00a40a1d | ||
|
|
54d7974863 | ||
|
|
1fd497fd55 | ||
|
|
26e961489f | ||
|
|
21a309290a | ||
|
|
08cb450d25 | ||
|
|
4bdd4f4f49 | ||
|
|
6d1b702214 | ||
|
|
bbe474ae57 | ||
|
|
bebf44d9ee | ||
|
|
231073c578 | ||
|
|
a00c343435 | ||
|
|
1e2e8844e5 | ||
|
|
6bc8080ec5 | ||
|
|
bff17ff470 | ||
|
|
c0a313abae | ||
|
|
db30bc51a1 | ||
|
|
c8c107405f | ||
|
|
6134cfd9c4 | ||
|
|
3f70105204 | ||
|
|
4ff08f942d | ||
|
|
29b75385a3 | ||
|
|
17de66140d | ||
|
|
f3d0fc4d68 | ||
|
|
0b636aec4b | ||
|
|
d0cddc5b66 | ||
|
|
cb0d72fc2f | ||
|
|
9a6be72c10 | ||
|
|
536d6ad360 | ||
|
|
b604a6ea8c | ||
|
|
da4672d715 | ||
|
|
74a919cb65 | ||
|
|
b92101a3da | ||
|
|
5057668705 | ||
|
|
931edd7419 | ||
|
|
044eaf7eb5 | ||
|
|
88c72a1514 | ||
|
|
d06cf09bf0 | ||
|
|
2f8e98242c | ||
|
|
1c6408081f | ||
|
|
3ff5a511e5 | ||
|
|
f3e976d67d | ||
|
|
21930a10ae | ||
|
|
82a6826a02 | ||
|
|
1c06ebadcd | ||
|
|
458b4a45e5 | ||
|
|
01039137b5 | ||
|
|
f86099c76e | ||
|
|
464be37815 | ||
|
|
f919d1654a | ||
|
|
7093c9a61b | ||
|
|
9ef3b0b994 | ||
|
|
6042625198 | ||
|
|
9d79a934bf | ||
|
|
a355292a7f | ||
|
|
24fabfff89 | ||
|
|
0b6ed44390 | ||
|
|
a6ce6dc7ab | ||
|
|
15984455af | ||
|
|
6e4bd564d5 | ||
|
|
aeabf3b188 | ||
|
|
c9d9c421bc | ||
|
|
d7d96b6b8b | ||
|
|
2631b908b6 | ||
|
|
502cc91dfe | ||
|
|
38e5eeea00 | ||
|
|
1ccbdb21e9 | ||
|
|
b1ef099cd6 | ||
|
|
00d46f1c8f | ||
|
|
8e304713a2 | ||
|
|
0899165d9e | ||
|
|
2d9982f9f0 | ||
|
|
b8fd98ab3c | ||
|
|
a27dfa1825 | ||
|
|
a3ece9d902 | ||
|
|
23613ac37e | ||
|
|
195337d865 | ||
|
|
4bb9f2ed7b | ||
|
|
e0ffddf3eb | ||
|
|
849f2c9818 | ||
|
|
8ebfaadeed | ||
|
|
1d49a46dd2 | ||
|
|
dabe6722aa | ||
|
|
10a63b3c23 | ||
|
|
9ce515a646 | ||
|
|
1df72ce2d0 | ||
|
|
6a960204b3 | ||
|
|
26cd13ae3c | ||
|
|
3793c6daca | ||
|
|
65f0d7930a | ||
|
|
8b914c02d0 |
@@ -1,60 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
plugins: ["matrix-org"],
|
|
||||||
extends: ["./.eslintrc.js"],
|
|
||||||
parserOptions: {
|
|
||||||
project: ["./tsconfig.module_system.json"],
|
|
||||||
},
|
|
||||||
overrides: [
|
|
||||||
{
|
|
||||||
files: ["module_system/**/*.{ts,tsx}"],
|
|
||||||
extends: ["plugin:matrix-org/typescript", "plugin:matrix-org/react"],
|
|
||||||
// NOTE: These rules are frozen and new rules should not be added here.
|
|
||||||
// New changes belong in https://github.com/matrix-org/eslint-plugin-matrix-org/
|
|
||||||
rules: {
|
|
||||||
// Things we do that break the ideal style
|
|
||||||
"prefer-promise-reject-errors": "off",
|
|
||||||
"quotes": "off",
|
|
||||||
|
|
||||||
// We disable this while we're transitioning
|
|
||||||
"@typescript-eslint/no-explicit-any": "off",
|
|
||||||
// We're okay with assertion errors when we ask for them
|
|
||||||
"@typescript-eslint/no-non-null-assertion": "off",
|
|
||||||
|
|
||||||
// Ban matrix-js-sdk/src imports in favour of matrix-js-sdk/src/matrix imports to prevent unleashing hell.
|
|
||||||
"no-restricted-imports": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
paths: [
|
|
||||||
{
|
|
||||||
name: "matrix-js-sdk",
|
|
||||||
message: "Please use matrix-js-sdk/src/matrix instead",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "matrix-js-sdk/",
|
|
||||||
message: "Please use matrix-js-sdk/src/matrix instead",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "matrix-js-sdk/src",
|
|
||||||
message: "Please use matrix-js-sdk/src/matrix instead",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "matrix-js-sdk/src/",
|
|
||||||
message: "Please use matrix-js-sdk/src/matrix instead",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "matrix-js-sdk/src/index",
|
|
||||||
message: "Please use matrix-js-sdk/src/matrix instead",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
patterns: [
|
|
||||||
{
|
|
||||||
group: ["matrix-js-sdk/lib", "matrix-js-sdk/lib/", "matrix-js-sdk/lib/**"],
|
|
||||||
message: "Please use matrix-js-sdk/src/* instead",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
71
.eslintrc.js
71
.eslintrc.js
@@ -1,5 +1,5 @@
|
|||||||
module.exports = {
|
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"],
|
extends: ["plugin:matrix-org/babel", "plugin:matrix-org/react", "plugin:matrix-org/a11y"],
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
project: ["./tsconfig.json"],
|
project: ["./tsconfig.json"],
|
||||||
@@ -42,6 +42,10 @@ module.exports = {
|
|||||||
name: "setImmediate",
|
name: "setImmediate",
|
||||||
message: "Use setTimeout instead.",
|
message: "Use setTimeout instead.",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Buffer",
|
||||||
|
message: "Buffer is not available in the web.",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
"import/no-duplicates": ["error"],
|
"import/no-duplicates": ["error"],
|
||||||
@@ -117,10 +121,6 @@ module.exports = {
|
|||||||
"!matrix-js-sdk/src/extensible_events_v1/PollResponseEvent",
|
"!matrix-js-sdk/src/extensible_events_v1/PollResponseEvent",
|
||||||
"!matrix-js-sdk/src/extensible_events_v1/PollEndEvent",
|
"!matrix-js-sdk/src/extensible_events_v1/PollEndEvent",
|
||||||
"!matrix-js-sdk/src/extensible_events_v1/InvalidEventError",
|
"!matrix-js-sdk/src/extensible_events_v1/InvalidEventError",
|
||||||
"!matrix-js-sdk/src/crypto",
|
|
||||||
"!matrix-js-sdk/src/crypto/keybackup",
|
|
||||||
"!matrix-js-sdk/src/crypto/deviceinfo",
|
|
||||||
"!matrix-js-sdk/src/crypto/dehydration",
|
|
||||||
"!matrix-js-sdk/src/oidc",
|
"!matrix-js-sdk/src/oidc",
|
||||||
"!matrix-js-sdk/src/oidc/discovery",
|
"!matrix-js-sdk/src/oidc/discovery",
|
||||||
"!matrix-js-sdk/src/oidc/authorize",
|
"!matrix-js-sdk/src/oidc/authorize",
|
||||||
@@ -170,6 +170,8 @@ module.exports = {
|
|||||||
"jsx-a11y/role-supports-aria-props": "off",
|
"jsx-a11y/role-supports-aria-props": "off",
|
||||||
|
|
||||||
"matrix-org/require-copyright-header": "error",
|
"matrix-org/require-copyright-header": "error",
|
||||||
|
|
||||||
|
"react-compiler/react-compiler": "error",
|
||||||
},
|
},
|
||||||
overrides: [
|
overrides: [
|
||||||
{
|
{
|
||||||
@@ -259,6 +261,10 @@ module.exports = {
|
|||||||
additionalTestBlockFunctions: ["beforeAll", "beforeEach", "oldBackendOnly"],
|
additionalTestBlockFunctions: ["beforeAll", "beforeEach", "oldBackendOnly"],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
|
// These are fine in tests
|
||||||
|
"no-restricted-globals": "off",
|
||||||
|
"react-compiler/react-compiler": "off",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -268,6 +274,61 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
"react-hooks/rules-of-hooks": ["off"],
|
"react-hooks/rules-of-hooks": ["off"],
|
||||||
|
"@typescript-eslint/no-floating-promises": ["error"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ["module_system/**/*.{ts,tsx}"],
|
||||||
|
parserOptions: {
|
||||||
|
project: ["./tsconfig.module_system.json"],
|
||||||
|
},
|
||||||
|
extends: ["plugin:matrix-org/typescript", "plugin:matrix-org/react"],
|
||||||
|
// NOTE: These rules are frozen and new rules should not be added here.
|
||||||
|
// New changes belong in https://github.com/matrix-org/eslint-plugin-matrix-org/
|
||||||
|
rules: {
|
||||||
|
// Things we do that break the ideal style
|
||||||
|
"prefer-promise-reject-errors": "off",
|
||||||
|
"quotes": "off",
|
||||||
|
|
||||||
|
// We disable this while we're transitioning
|
||||||
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
// We're okay with assertion errors when we ask for them
|
||||||
|
"@typescript-eslint/no-non-null-assertion": "off",
|
||||||
|
|
||||||
|
// Ban matrix-js-sdk/src imports in favour of matrix-js-sdk/src/matrix imports to prevent unleashing hell.
|
||||||
|
"no-restricted-imports": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
paths: [
|
||||||
|
{
|
||||||
|
name: "matrix-js-sdk",
|
||||||
|
message: "Please use matrix-js-sdk/src/matrix instead",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "matrix-js-sdk/",
|
||||||
|
message: "Please use matrix-js-sdk/src/matrix instead",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "matrix-js-sdk/src",
|
||||||
|
message: "Please use matrix-js-sdk/src/matrix instead",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "matrix-js-sdk/src/",
|
||||||
|
message: "Please use matrix-js-sdk/src/matrix instead",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "matrix-js-sdk/src/index",
|
||||||
|
message: "Please use matrix-js-sdk/src/matrix instead",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
patterns: [
|
||||||
|
{
|
||||||
|
group: ["matrix-js-sdk/lib", "matrix-js-sdk/lib/", "matrix-js-sdk/lib/**"],
|
||||||
|
message: "Please use matrix-js-sdk/src/* instead",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
7
.github/CODEOWNERS
vendored
7
.github/CODEOWNERS
vendored
@@ -10,9 +10,14 @@
|
|||||||
/test/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
|
/src/stores/SetupEncryptionStore.ts @element-hq/element-crypto-web-reviewers
|
||||||
/test/stores/SetupEncryptionStore-test.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
|
||||||
|
|
||||||
# Ignore translations as those will be updated by GHA for Localazy download
|
# Ignore translations as those will be updated by GHA for Localazy download
|
||||||
/src/i18n/strings
|
/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
|
# Ignore the synapse plugin as this is updated by GHA for docker image updating
|
||||||
/playwright/plugins/homeserver/synapse/index.ts
|
/playwright/testcontainers/synapse.ts
|
||||||
|
|
||||||
|
|||||||
38
.github/actions/download-verify-element-tarball/action.yml
vendored
Normal file
38
.github/actions/download-verify-element-tarball/action.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
name: Upload release assets
|
||||||
|
description: Uploads assets to an existing release and optionally signs them
|
||||||
|
inputs:
|
||||||
|
tag:
|
||||||
|
description: GitHub release tag to fetch assets from.
|
||||||
|
required: true
|
||||||
|
out-file-path:
|
||||||
|
description: Path to where the webapp should be extracted to.
|
||||||
|
required: true
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: Download release tarball
|
||||||
|
uses: robinraju/release-downloader@a96f54c1b5f5e09e47d9504526e96febd949d4c2 # v1
|
||||||
|
with:
|
||||||
|
tag: ${{ inputs.tag }}
|
||||||
|
fileName: element-*.tar.gz*
|
||||||
|
out-file-path: ${{ runner.temp }}/download-verify-element-tarball
|
||||||
|
|
||||||
|
- name: Verify tarball
|
||||||
|
shell: bash
|
||||||
|
run: gpg --verify element-*.tar.gz.asc element-*.tar.gz
|
||||||
|
working-directory: ${{ runner.temp }}/download-verify-element-tarball
|
||||||
|
|
||||||
|
- name: Extract tarball
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
mkdir webapp
|
||||||
|
tar xvzf element-*.tar.gz -C webapp --strip-components=1
|
||||||
|
working-directory: ${{ runner.temp }}/download-verify-element-tarball
|
||||||
|
|
||||||
|
- name: Move webapp to out-file-path
|
||||||
|
shell: bash
|
||||||
|
run: mv ${{ runner.temp }}/download-verify-element-tarball/webapp ${{ inputs.out-file-path }}
|
||||||
|
|
||||||
|
- name: Clean up temp directory
|
||||||
|
shell: bash
|
||||||
|
run: rm -R ${{ runner.temp }}/download-verify-element-tarball
|
||||||
15
.github/labels.yml
vendored
15
.github/labels.yml
vendored
@@ -210,6 +210,9 @@
|
|||||||
- name: "X-Upcoming-Release-Blocker"
|
- name: "X-Upcoming-Release-Blocker"
|
||||||
description: "This does not affect the current release cycle but will affect the next one"
|
description: "This does not affect the current release cycle but will affect the next one"
|
||||||
color: "e99695"
|
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"
|
- name: "Z-Actions"
|
||||||
color: "ededed"
|
color: "ededed"
|
||||||
- name: "Z-Cache-Confusion"
|
- name: "Z-Cache-Confusion"
|
||||||
@@ -232,6 +235,18 @@
|
|||||||
- name: "Z-Flaky-Test"
|
- name: "Z-Flaky-Test"
|
||||||
description: "A test is raising false alarms"
|
description: "A test is raising false alarms"
|
||||||
color: "ededed"
|
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"
|
||||||
- name: "Z-FOSDEM"
|
- name: "Z-FOSDEM"
|
||||||
description: "Issues in chat.fosdem.org"
|
description: "Issues in chat.fosdem.org"
|
||||||
color: "ededed"
|
color: "ededed"
|
||||||
|
|||||||
2
.github/workflows/backport.yml
vendored
2
.github/workflows/backport.yml
vendored
@@ -7,6 +7,8 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- develop
|
- develop
|
||||||
|
|
||||||
|
permissions: {} # We use ELEMENT_BOT_TOKEN instead
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
backport:
|
backport:
|
||||||
name: Backport
|
name: Backport
|
||||||
|
|||||||
43
.github/workflows/build.yml
vendored
43
.github/workflows/build.yml
vendored
@@ -5,11 +5,15 @@ on:
|
|||||||
branches: [develop, master]
|
branches: [develop, master]
|
||||||
merge_group:
|
merge_group:
|
||||||
types: [checks_requested]
|
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
|
# develop pushes and repository_dispatch handled in build_develop.yaml
|
||||||
env:
|
env:
|
||||||
# These must be set for fetchdep.sh to get the right branch
|
# These must be set for fetchdep.sh to get the right branch
|
||||||
REPOSITORY: ${{ github.repository }}
|
REPOSITORY: ${{ github.repository }}
|
||||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||||
|
permissions: {} # No permissions required
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: "Build on ${{ matrix.image }}"
|
name: "Build on ${{ matrix.image }}"
|
||||||
@@ -23,10 +27,17 @@ jobs:
|
|||||||
- macos-14
|
- macos-14
|
||||||
isDevelop:
|
isDevelop:
|
||||||
- ${{ github.event_name == 'push' && github.ref_name == 'develop' }}
|
- ${{ 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 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:
|
exclude:
|
||||||
- isDevelop: true
|
- isDevelop: true
|
||||||
image: ubuntu-24.04
|
image: ubuntu-24.04
|
||||||
|
- isPullRequest: true
|
||||||
|
image: windows-2022
|
||||||
|
- isPullRequest: true
|
||||||
|
image: macos-14
|
||||||
runs-on: ${{ matrix.image }}
|
runs-on: ${{ matrix.image }}
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
@@ -36,14 +47,38 @@ jobs:
|
|||||||
|
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
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/*"
|
node-version: "lts/*"
|
||||||
|
|
||||||
# Workaround for yarn install timeouts, especially on Windows
|
# Workaround for yarn install timeouts, especially on Windows
|
||||||
- run: yarn config set network-timeout 300000
|
- run: yarn config set network-timeout 300000
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Fetch layered build
|
||||||
run: "./scripts/layered.sh"
|
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
|
- 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
|
||||||
|
|||||||
1
.github/workflows/build_debian.yaml
vendored
1
.github/workflows/build_debian.yaml
vendored
@@ -3,6 +3,7 @@ on:
|
|||||||
release:
|
release:
|
||||||
types: [published]
|
types: [published]
|
||||||
concurrency: ${{ github.workflow }}
|
concurrency: ${{ github.workflow }}
|
||||||
|
permissions: {} # We use ELEMENT_BOT_TOKEN instead
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build package
|
name: Build package
|
||||||
|
|||||||
5
.github/workflows/build_develop.yml
vendored
5
.github/workflows/build_develop.yml
vendored
@@ -9,6 +9,7 @@ on:
|
|||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.repository_owner }}-${{ github.workflow }}-${{ github.ref_name }}
|
group: ${{ github.repository_owner }}-${{ github.workflow }}-${{ github.ref_name }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
permissions: {}
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: "Build & Deploy develop.element.io"
|
name: "Build & Deploy develop.element.io"
|
||||||
@@ -16,6 +17,10 @@ jobs:
|
|||||||
if: github.repository == 'element-hq/element-web'
|
if: github.repository == 'element-hq/element-web'
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
environment: develop
|
environment: develop
|
||||||
|
permissions:
|
||||||
|
checks: read
|
||||||
|
pages: write
|
||||||
|
deployments: write
|
||||||
env:
|
env:
|
||||||
R2_BUCKET: "element-web-develop"
|
R2_BUCKET: "element-web-develop"
|
||||||
R2_URL: ${{ vars.CF_R2_S3_API }}
|
R2_URL: ${{ vars.CF_R2_S3_API }}
|
||||||
|
|||||||
99
.github/workflows/deploy.yml
vendored
Normal file
99
.github/workflows/deploy.yml
vendored
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
# Manual deploy workflow for deploying to app.element.io & staging.element.io
|
||||||
|
# Runs automatically for staging.element.io when an RC or Release is published
|
||||||
|
# Note: Does *NOT* run automatically for app.element.io so that it gets tested on staging.element.io beforehand
|
||||||
|
name: Deploy release
|
||||||
|
run-name: Deploy ${{ github.ref_name }} to ${{ inputs.site || 'staging.element.io' }}
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
site:
|
||||||
|
description: Which site to deploy to
|
||||||
|
required: true
|
||||||
|
default: staging.element.io
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- staging.element.io
|
||||||
|
- app.element.io
|
||||||
|
skip-checks:
|
||||||
|
description: Skip CI on the tagged commit
|
||||||
|
required: true
|
||||||
|
default: false
|
||||||
|
type: boolean
|
||||||
|
concurrency: ${{ inputs.site || 'staging.element.io' }}
|
||||||
|
permissions: {}
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
name: "Deploy to Cloudflare Pages"
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
environment: ${{ inputs.site || 'staging.element.io' }}
|
||||||
|
permissions:
|
||||||
|
checks: read
|
||||||
|
deployments: write
|
||||||
|
env:
|
||||||
|
SITE: ${{ inputs.site || 'staging.element.io' }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Load GPG key
|
||||||
|
run: |
|
||||||
|
curl https://packages.element.io/element-release-key.gpg | gpg --import
|
||||||
|
gpg -k "$GPG_FINGERPRINT"
|
||||||
|
env:
|
||||||
|
GPG_FINGERPRINT: ${{ vars.GPG_FINGERPRINT }}
|
||||||
|
|
||||||
|
- name: Check current version on deployment
|
||||||
|
id: current_version
|
||||||
|
run: |
|
||||||
|
version=$(curl -s https://$SITE/version)
|
||||||
|
echo "version=${version#v}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
# The current version bundle melding dance is skipped if the version we're deploying is the same
|
||||||
|
# as then we're just doing a re-deploy of the same version with potentially different configs.
|
||||||
|
- name: Download current version for its old bundles
|
||||||
|
id: current_download
|
||||||
|
if: steps.current_version.outputs.version != github.ref_name
|
||||||
|
uses: ./.github/actions/download-verify-element-tarball
|
||||||
|
with:
|
||||||
|
tag: v${{ steps.current_version.outputs.version }}
|
||||||
|
out-file-path: _current_version
|
||||||
|
|
||||||
|
- name: Download target version
|
||||||
|
uses: ./.github/actions/download-verify-element-tarball
|
||||||
|
with:
|
||||||
|
tag: ${{ github.ref_name }}
|
||||||
|
out-file-path: _deploy
|
||||||
|
|
||||||
|
- name: Merge current bundles into target
|
||||||
|
if: steps.current_download.outcome == 'success'
|
||||||
|
run: cp -vnpr _current_version/bundles/* _deploy/bundles/
|
||||||
|
|
||||||
|
- name: Copy config
|
||||||
|
run: cp element.io/app/config.json _deploy/config.json
|
||||||
|
|
||||||
|
- name: Populate 404.html
|
||||||
|
run: echo "404 Not Found" > _deploy/404.html
|
||||||
|
|
||||||
|
- name: Populate _headers
|
||||||
|
run: cp .github/cfp_headers _deploy/_headers
|
||||||
|
|
||||||
|
- name: Wait for other steps to succeed
|
||||||
|
uses: t3chguy/wait-on-check-action@18541021811b56544d90e0f073401c2b99e249d6 # fork
|
||||||
|
if: inputs.skip-checks != true
|
||||||
|
with:
|
||||||
|
ref: ${{ github.sha }}
|
||||||
|
running-workflow-name: "Deploy to Cloudflare Pages"
|
||||||
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
wait-interval: 10
|
||||||
|
check-regexp: ^((?!SonarCloud|SonarQube|issue|board|label|Release|prepare|GitHub Pages).)*$
|
||||||
|
|
||||||
|
- name: Deploy to Cloudflare Pages
|
||||||
|
uses: cloudflare/pages-action@f0a1cd58cd66095dee69bfa18fa5efd1dde93bca # v1
|
||||||
|
with:
|
||||||
|
apiToken: ${{ secrets.CF_PAGES_TOKEN }}
|
||||||
|
accountId: ${{ secrets.CF_PAGES_ACCOUNT_ID }}
|
||||||
|
projectName: ${{ env.SITE == 'staging.element.io' && 'element-web-staging' || 'element-web' }}
|
||||||
|
directory: _deploy
|
||||||
|
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
branch: main
|
||||||
16
.github/workflows/dockerhub.yaml
vendored
16
.github/workflows/dockerhub.yaml
vendored
@@ -7,27 +7,27 @@ on:
|
|||||||
# This job can take a while, and we have usage limits, so just publish develop only twice a day
|
# This job can take a while, and we have usage limits, so just publish develop only twice a day
|
||||||
- cron: "0 7/12 * * *"
|
- cron: "0 7/12 * * *"
|
||||||
concurrency: ${{ github.workflow }}-${{ github.ref_name }}
|
concurrency: ${{ github.workflow }}-${{ github.ref_name }}
|
||||||
|
permissions: {}
|
||||||
permissions:
|
|
||||||
id-token: write # needed for signing the images with GitHub OIDC Token
|
|
||||||
jobs:
|
jobs:
|
||||||
buildx:
|
buildx:
|
||||||
name: Docker Buildx
|
name: Docker Buildx
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
environment: dockerhub
|
environment: dockerhub
|
||||||
|
permissions:
|
||||||
|
id-token: write # needed for signing the images with GitHub OIDC Token
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0 # needed for docker-package to be able to calculate the version
|
fetch-depth: 0 # needed for docker-package to be able to calculate the version
|
||||||
|
|
||||||
- name: Install Cosign
|
- name: Install Cosign
|
||||||
uses: sigstore/cosign-installer@4959ce089c160fddf62f7b42464195ba1a56d382 # v3
|
uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3
|
uses: docker/setup-qemu-action@53851d14592bedcffcf25ea515637cff71ef929a # v3
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3
|
uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3
|
||||||
with:
|
with:
|
||||||
install: true
|
install: true
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5
|
uses: docker/metadata-action@369eb591f429131d6889c46b94e711f089e6ca96 # v5
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
vectorim/element-web
|
vectorim/element-web
|
||||||
@@ -51,7 +51,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
id: build-and-push
|
id: build-and-push
|
||||||
uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6
|
uses: docker/build-push-action@67a2d409c0a876cbe6b11854e3e25193efe4e62d # v6
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: true
|
push: true
|
||||||
|
|||||||
8
.github/workflows/docs.yml
vendored
8
.github/workflows/docs.yml
vendored
@@ -5,10 +5,7 @@ on:
|
|||||||
branches: [develop]
|
branches: [develop]
|
||||||
workflow_dispatch: {}
|
workflow_dispatch: {}
|
||||||
|
|
||||||
permissions:
|
permissions: {}
|
||||||
contents: read
|
|
||||||
pages: write
|
|
||||||
id-token: write
|
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: "pages"
|
group: "pages"
|
||||||
@@ -100,6 +97,9 @@ jobs:
|
|||||||
name: github-pages
|
name: github-pages
|
||||||
url: ${{ steps.deployment.outputs.page_url }}
|
url: ${{ steps.deployment.outputs.page_url }}
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
|
permissions:
|
||||||
|
pages: write
|
||||||
|
id-token: write
|
||||||
needs: build
|
needs: build
|
||||||
steps:
|
steps:
|
||||||
- name: Deploy to GitHub Pages
|
- name: Deploy to GitHub Pages
|
||||||
|
|||||||
@@ -11,20 +11,23 @@ concurrency:
|
|||||||
group: ${{ github.workflow }}-${{ github.event.workflow_run.head_branch || github.run_id }}
|
group: ${{ github.workflow }}-${{ github.event.workflow_run.head_branch || github.run_id }}
|
||||||
cancel-in-progress: ${{ github.event.workflow_run.event == 'pull_request' }}
|
cancel-in-progress: ${{ github.event.workflow_run.event == 'pull_request' }}
|
||||||
|
|
||||||
|
permissions: {}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
report:
|
report:
|
||||||
if: github.event.workflow_run.conclusion != 'cancelled'
|
if: github.event.workflow_run.conclusion != 'cancelled'
|
||||||
name: Report results
|
name: Report results
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
environment: Netlify
|
environment: Netlify
|
||||||
permissions:
|
permissions:
|
||||||
statuses: write
|
statuses: write
|
||||||
deployments: write
|
deployments: write
|
||||||
|
actions: read
|
||||||
steps:
|
steps:
|
||||||
- name: Download HTML report
|
- name: Download HTML report
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run-id: ${{ github.event.workflow_run.id }}
|
run-id: ${{ github.event.workflow_run.id }}
|
||||||
name: html-report
|
name: html-report
|
||||||
path: playwright-report
|
path: playwright-report
|
||||||
|
|||||||
64
.github/workflows/end-to-end-tests.yaml
vendored
64
.github/workflows/end-to-end-tests.yaml
vendored
@@ -3,6 +3,9 @@
|
|||||||
# as an artifact and run end-to-end tests.
|
# as an artifact and run end-to-end tests.
|
||||||
name: End to End Tests
|
name: End to End Tests
|
||||||
on:
|
on:
|
||||||
|
# CRON to run all Projects at 6am UTC
|
||||||
|
schedule:
|
||||||
|
- cron: "0 6 * * *"
|
||||||
pull_request: {}
|
pull_request: {}
|
||||||
merge_group:
|
merge_group:
|
||||||
types: [checks_requested]
|
types: [checks_requested]
|
||||||
@@ -32,12 +35,19 @@ concurrency:
|
|||||||
env:
|
env:
|
||||||
# fetchdep.sh needs to know our PR number
|
# fetchdep.sh needs to know our PR number
|
||||||
PR_NUMBER: ${{ github.event.pull_request.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
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: "Build Element-Web"
|
name: "Build Element-Web"
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
if: inputs.skip != true
|
if: inputs.skip != true
|
||||||
|
outputs:
|
||||||
|
num-runners: ${{ env.NUM_RUNNERS }}
|
||||||
|
runners-matrix: ${{ steps.runner-vars.outputs.matrix }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -69,7 +79,6 @@ jobs:
|
|||||||
VERSION: "${{ steps.layered_build.outputs.VERSION }}"
|
VERSION: "${{ steps.layered_build.outputs.VERSION }}"
|
||||||
run: |
|
run: |
|
||||||
yarn build
|
yarn build
|
||||||
echo $VERSION > webapp/version
|
|
||||||
|
|
||||||
- name: Upload Artifact
|
- name: Upload Artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
@@ -78,11 +87,20 @@ jobs:
|
|||||||
path: webapp
|
path: webapp
|
||||||
retention-days: 1
|
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:
|
playwright:
|
||||||
name: "Run Tests ${{ matrix.runner }}/${{ strategy.job-total }}"
|
name: "Run Tests [${{ matrix.project }}] ${{ matrix.runner }}/${{ needs.build.outputs.num-runners }}"
|
||||||
needs: build
|
needs: build
|
||||||
if: inputs.skip != true
|
if: inputs.skip != true
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
permissions:
|
permissions:
|
||||||
actions: read
|
actions: read
|
||||||
issues: read
|
issues: read
|
||||||
@@ -91,7 +109,25 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
# Run multiple instances in parallel to speed up the tests
|
# 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:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
@@ -127,16 +163,26 @@ jobs:
|
|||||||
|
|
||||||
- name: Install Playwright browsers
|
- name: Install Playwright browsers
|
||||||
if: steps.playwright-cache.outputs.cache-hit != 'true'
|
if: steps.playwright-cache.outputs.cache-hit != 'true'
|
||||||
run: yarn playwright install --with-deps
|
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
|
- name: Run Playwright tests
|
||||||
run: yarn playwright test --shard ${{ matrix.runner }}/${{ strategy.job-total }}
|
run: |
|
||||||
|
yarn playwright test \
|
||||||
|
--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
|
- name: Upload blob report to GitHub Actions Artifacts
|
||||||
if: always()
|
if: always()
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: all-blob-reports-${{ matrix.runner }}
|
name: all-blob-reports-${{ matrix.project }}-${{ matrix.runner }}
|
||||||
path: blob-report
|
path: blob-report
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
|
|
||||||
@@ -144,7 +190,7 @@ jobs:
|
|||||||
name: end-to-end-tests
|
name: end-to-end-tests
|
||||||
needs: playwright
|
needs: playwright
|
||||||
if: always()
|
if: always()
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
if: inputs.skip != true
|
if: inputs.skip != true
|
||||||
|
|||||||
1
.github/workflows/issue_closed.yml
vendored
1
.github/workflows/issue_closed.yml
vendored
@@ -4,6 +4,7 @@
|
|||||||
on:
|
on:
|
||||||
issues:
|
issues:
|
||||||
types: [closed]
|
types: [closed]
|
||||||
|
permissions: {} # We use ELEMENT_BOT_TOKEN instead
|
||||||
jobs:
|
jobs:
|
||||||
tidy:
|
tidy:
|
||||||
name: Tidy closed issues
|
name: Tidy closed issues
|
||||||
|
|||||||
2
.github/workflows/localazy_download.yaml
vendored
2
.github/workflows/localazy_download.yaml
vendored
@@ -3,6 +3,8 @@ on:
|
|||||||
workflow_dispatch: {}
|
workflow_dispatch: {}
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "0 6 * * 1,3,5" # Every Monday, Wednesday and Friday at 6am UTC
|
- cron: "0 6 * * 1,3,5" # Every Monday, Wednesday and Friday at 6am UTC
|
||||||
|
permissions:
|
||||||
|
pull-requests: write # needed to auto-approve PRs
|
||||||
jobs:
|
jobs:
|
||||||
download:
|
download:
|
||||||
uses: matrix-org/matrix-web-i18n/.github/workflows/localazy_download.yaml@main
|
uses: matrix-org/matrix-web-i18n/.github/workflows/localazy_download.yaml@main
|
||||||
|
|||||||
1
.github/workflows/localazy_upload.yaml
vendored
1
.github/workflows/localazy_upload.yaml
vendored
@@ -4,6 +4,7 @@ on:
|
|||||||
branches: [develop]
|
branches: [develop]
|
||||||
paths:
|
paths:
|
||||||
- "src/i18n/strings/en_EN.json"
|
- "src/i18n/strings/en_EN.json"
|
||||||
|
permissions: {} # No permissions needed
|
||||||
jobs:
|
jobs:
|
||||||
upload:
|
upload:
|
||||||
uses: matrix-org/matrix-web-i18n/.github/workflows/localazy_upload.yaml@main
|
uses: matrix-org/matrix-web-i18n/.github/workflows/localazy_upload.yaml@main
|
||||||
|
|||||||
11
.github/workflows/netlify.yaml
vendored
11
.github/workflows/netlify.yaml
vendored
@@ -3,14 +3,17 @@
|
|||||||
name: Upload Preview Build to Netlify
|
name: Upload Preview Build to Netlify
|
||||||
on:
|
on:
|
||||||
workflow_run:
|
workflow_run:
|
||||||
workflows: ["End to End Tests"]
|
workflows: ["Build"]
|
||||||
types:
|
types:
|
||||||
- completed
|
- completed
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
if: github.event.workflow_run.conclusion != 'cancelled' && github.event.workflow_run.event == 'pull_request'
|
if: github.event.workflow_run.conclusion != 'cancelled' && github.event.workflow_run.event == 'pull_request'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
environment: Netlify
|
environment: Netlify
|
||||||
|
permissions:
|
||||||
|
actions: read
|
||||||
|
deployments: write
|
||||||
steps:
|
steps:
|
||||||
- name: 📝 Create Deployment
|
- name: 📝 Create Deployment
|
||||||
uses: bobheadxi/deployments@648679e8e4915b27893bd7dbc35cb504dc915bc8 # v1
|
uses: bobheadxi/deployments@648679e8e4915b27893bd7dbc35cb504dc915bc8 # v1
|
||||||
@@ -27,9 +30,9 @@ jobs:
|
|||||||
- name: 📥 Download artifact
|
- name: 📥 Download artifact
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run-id: ${{ github.event.workflow_run.id }}
|
run-id: ${{ github.event.workflow_run.id }}
|
||||||
name: webapp
|
name: webapp-ubuntu-24.04
|
||||||
path: webapp
|
path: webapp
|
||||||
|
|
||||||
- name: 📤 Deploy to Netlify
|
- name: 📤 Deploy to Netlify
|
||||||
|
|||||||
1
.github/workflows/pending-reviews.yaml
vendored
1
.github/workflows/pending-reviews.yaml
vendored
@@ -6,6 +6,7 @@ on:
|
|||||||
#schedule:
|
#schedule:
|
||||||
# - cron: "*/10 * * * *"
|
# - cron: "*/10 * * * *"
|
||||||
concurrency: ${{ github.workflow }}
|
concurrency: ${{ github.workflow }}
|
||||||
|
permissions: {} # We use ELEMENT_BOT_TOKEN instead
|
||||||
jobs:
|
jobs:
|
||||||
bot:
|
bot:
|
||||||
name: Pending reviews bot
|
name: Pending reviews bot
|
||||||
|
|||||||
@@ -3,9 +3,12 @@ on:
|
|||||||
workflow_dispatch: {}
|
workflow_dispatch: {}
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "0 6 * * *" # Every day at 6am UTC
|
- cron: "0 6 * * *" # Every day at 6am UTC
|
||||||
|
permissions: {}
|
||||||
jobs:
|
jobs:
|
||||||
update:
|
update:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
@@ -14,13 +17,13 @@ jobs:
|
|||||||
docker pull "$IMAGE"
|
docker pull "$IMAGE"
|
||||||
INSPECT=$(docker inspect --format='{{index .RepoDigests 0}}' "$IMAGE")
|
INSPECT=$(docker inspect --format='{{index .RepoDigests 0}}' "$IMAGE")
|
||||||
DIGEST=${INSPECT#*@}
|
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:
|
env:
|
||||||
IMAGE: ghcr.io/element-hq/synapse:develop
|
IMAGE: ghcr.io/element-hq/synapse:develop
|
||||||
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
id: cpr
|
id: cpr
|
||||||
uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7
|
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
branch: actions/playwright-image-updates
|
branch: actions/playwright-image-updates
|
||||||
|
|||||||
3
.github/workflows/pull_request.yaml
vendored
3
.github/workflows/pull_request.yaml
vendored
@@ -4,8 +4,11 @@ on:
|
|||||||
types: [opened, edited, labeled, unlabeled, synchronize]
|
types: [opened, edited, labeled, unlabeled, synchronize]
|
||||||
merge_group:
|
merge_group:
|
||||||
types: [checks_requested]
|
types: [checks_requested]
|
||||||
|
permissions: {}
|
||||||
jobs:
|
jobs:
|
||||||
action:
|
action:
|
||||||
uses: matrix-org/matrix-js-sdk/.github/workflows/pull_request.yaml@develop
|
uses: matrix-org/matrix-js-sdk/.github/workflows/pull_request.yaml@develop
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
secrets:
|
secrets:
|
||||||
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ name: Pull Request Base Branch
|
|||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
types: [opened, edited, synchronize]
|
types: [opened, edited, synchronize]
|
||||||
|
permissions: {} # No permissions required
|
||||||
jobs:
|
jobs:
|
||||||
check_base_branch:
|
check_base_branch:
|
||||||
name: Check PR base branch
|
name: Check PR base branch
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@v7
|
- uses: actions/github-script@v7
|
||||||
with:
|
with:
|
||||||
|
|||||||
3
.github/workflows/release-drafter.yml
vendored
3
.github/workflows/release-drafter.yml
vendored
@@ -4,6 +4,9 @@ on:
|
|||||||
branches: [staging]
|
branches: [staging]
|
||||||
workflow_dispatch: {}
|
workflow_dispatch: {}
|
||||||
concurrency: ${{ github.workflow }}
|
concurrency: ${{ github.workflow }}
|
||||||
|
permissions: {}
|
||||||
jobs:
|
jobs:
|
||||||
draft:
|
draft:
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
uses: matrix-org/matrix-js-sdk/.github/workflows/release-drafter-workflow.yml@develop
|
uses: matrix-org/matrix-js-sdk/.github/workflows/release-drafter-workflow.yml@develop
|
||||||
|
|||||||
1
.github/workflows/release-gitflow.yml
vendored
1
.github/workflows/release-gitflow.yml
vendored
@@ -4,6 +4,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches: [master]
|
branches: [master]
|
||||||
concurrency: ${{ github.repository }}-${{ github.workflow }}
|
concurrency: ${{ github.repository }}-${{ github.workflow }}
|
||||||
|
permissions: {} # We use ELEMENT_BOT_TOKEN instead
|
||||||
jobs:
|
jobs:
|
||||||
merge:
|
merge:
|
||||||
uses: matrix-org/matrix-js-sdk/.github/workflows/release-gitflow.yml@develop
|
uses: matrix-org/matrix-js-sdk/.github/workflows/release-gitflow.yml@develop
|
||||||
|
|||||||
9
.github/workflows/release.yml
vendored
9
.github/workflows/release.yml
vendored
@@ -11,9 +11,14 @@ on:
|
|||||||
- rc
|
- rc
|
||||||
- final
|
- final
|
||||||
concurrency: ${{ github.workflow }}
|
concurrency: ${{ github.workflow }}
|
||||||
|
permissions: {}
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
uses: matrix-org/matrix-js-sdk/.github/workflows/release-make.yml@develop
|
uses: matrix-org/matrix-js-sdk/.github/workflows/release-make.yml@develop
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
issues: write
|
||||||
|
pull-requests: read
|
||||||
secrets:
|
secrets:
|
||||||
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||||
@@ -42,6 +47,8 @@ jobs:
|
|||||||
name: Post release checks
|
name: Post release checks
|
||||||
needs: release
|
needs: release
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
|
permissions:
|
||||||
|
checks: read
|
||||||
steps:
|
steps:
|
||||||
- name: Wait for dockerhub
|
- name: Wait for dockerhub
|
||||||
uses: t3chguy/wait-on-check-action@18541021811b56544d90e0f073401c2b99e249d6 # fork
|
uses: t3chguy/wait-on-check-action@18541021811b56544d90e0f073401c2b99e249d6 # fork
|
||||||
@@ -49,7 +56,7 @@ jobs:
|
|||||||
ref: master
|
ref: master
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
wait-interval: 10
|
wait-interval: 10
|
||||||
check-name: "Docker Buildx (vanilla)"
|
check-name: "Docker Buildx"
|
||||||
allowed-conclusions: success
|
allowed-conclusions: success
|
||||||
|
|
||||||
- name: Wait for debian package
|
- name: Wait for debian package
|
||||||
|
|||||||
16
.github/workflows/release_prepare.yml
vendored
16
.github/workflows/release_prepare.yml
vendored
@@ -17,9 +17,25 @@ on:
|
|||||||
required: true
|
required: true
|
||||||
type: boolean
|
type: boolean
|
||||||
default: true
|
default: true
|
||||||
|
permissions: {} # Uses ELEMENT_BOT_TOKEN instead
|
||||||
jobs:
|
jobs:
|
||||||
|
checks:
|
||||||
|
name: Sanity checks
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
repo:
|
||||||
|
- matrix-org/matrix-js-sdk
|
||||||
|
- element-hq/element-web
|
||||||
|
- element-hq/element-desktop
|
||||||
|
uses: matrix-org/matrix-js-sdk/.github/workflows/release-checks.yml@develop
|
||||||
|
secrets:
|
||||||
|
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
|
with:
|
||||||
|
repository: ${{ matrix.repo }}
|
||||||
|
|
||||||
prepare:
|
prepare:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
|
needs: checks
|
||||||
env:
|
env:
|
||||||
# The order is specified bottom-up to avoid any races for allchange
|
# The order is specified bottom-up to avoid any races for allchange
|
||||||
REPOS: matrix-js-sdk element-web element-desktop
|
REPOS: matrix-js-sdk element-web element-desktop
|
||||||
|
|||||||
5
.github/workflows/sonarqube.yml
vendored
5
.github/workflows/sonarqube.yml
vendored
@@ -7,11 +7,16 @@ on:
|
|||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.event.workflow_run.head_branch }}
|
group: ${{ github.workflow }}-${{ github.event.workflow_run.head_branch }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
permissions: {}
|
||||||
jobs:
|
jobs:
|
||||||
sonarqube:
|
sonarqube:
|
||||||
name: 🩻 SonarQube
|
name: 🩻 SonarQube
|
||||||
if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event != 'merge_group'
|
if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event != 'merge_group'
|
||||||
uses: matrix-org/matrix-js-sdk/.github/workflows/sonarcloud.yml@develop
|
uses: matrix-org/matrix-js-sdk/.github/workflows/sonarcloud.yml@develop
|
||||||
|
permissions:
|
||||||
|
actions: read
|
||||||
|
statuses: write
|
||||||
|
id-token: write # sonar
|
||||||
secrets:
|
secrets:
|
||||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||||
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
|
|||||||
33
.github/workflows/static_analysis.yaml
vendored
33
.github/workflows/static_analysis.yaml
vendored
@@ -16,6 +16,8 @@ env:
|
|||||||
REPOSITORY: ${{ github.repository }}
|
REPOSITORY: ${{ github.repository }}
|
||||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||||
|
|
||||||
|
permissions: {} # No permissions required
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
ts_lint:
|
ts_lint:
|
||||||
name: "Typescript Syntax Check"
|
name: "Typescript Syntax Check"
|
||||||
@@ -34,30 +36,11 @@ jobs:
|
|||||||
- name: Typecheck
|
- name: Typecheck
|
||||||
run: "yarn run lint:types"
|
run: "yarn run lint:types"
|
||||||
|
|
||||||
- name: Switch js-sdk to release mode
|
|
||||||
working-directory: node_modules/matrix-js-sdk
|
|
||||||
run: |
|
|
||||||
scripts/switch_package_to_release.cjs
|
|
||||||
yarn install
|
|
||||||
yarn run build:compile
|
|
||||||
yarn run build:types
|
|
||||||
|
|
||||||
- name: Typecheck (release mode)
|
|
||||||
run: "yarn run lint:types"
|
|
||||||
|
|
||||||
# Temporary while we directly import matrix-js-sdk/src/* which means we need
|
|
||||||
# certain @types/* packages to make sense of matrix-js-sdk types.
|
|
||||||
#- name: Typecheck (release mode; no yarn link)
|
|
||||||
# if: github.event_name != 'pull_request' && github.ref_name != 'master'
|
|
||||||
# run: |
|
|
||||||
# yarn unlink matrix-js-sdk
|
|
||||||
# yarn add github:matrix-org/matrix-js-sdk#develop
|
|
||||||
# yarn install --force
|
|
||||||
# yarn run lint:types
|
|
||||||
|
|
||||||
i18n_lint:
|
i18n_lint:
|
||||||
name: "i18n Check"
|
name: "i18n Check"
|
||||||
uses: matrix-org/matrix-web-i18n/.github/workflows/i18n_check.yml@main
|
uses: matrix-org/matrix-web-i18n/.github/workflows/i18n_check.yml@main
|
||||||
|
permissions:
|
||||||
|
pull-requests: read
|
||||||
with:
|
with:
|
||||||
hardcoded-words: "Element"
|
hardcoded-words: "Element"
|
||||||
allowed-hardcoded-keys: |
|
allowed-hardcoded-keys: |
|
||||||
@@ -71,7 +54,7 @@ jobs:
|
|||||||
|
|
||||||
rethemendex_lint:
|
rethemendex_lint:
|
||||||
name: "Rethemendex Check"
|
name: "Rethemendex Check"
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
@@ -145,7 +128,7 @@ jobs:
|
|||||||
node-version: "lts/*"
|
node-version: "lts/*"
|
||||||
|
|
||||||
- name: Install Deps
|
- name: Install Deps
|
||||||
run: "scripts/layered.sh"
|
run: "yarn install --frozen-lockfile"
|
||||||
|
|
||||||
- name: Dead Code Analysis
|
- name: Run linter
|
||||||
run: "yarn run analyse:unused-exports"
|
run: "yarn run lint:knip"
|
||||||
|
|||||||
3
.github/workflows/sync-labels.yml
vendored
3
.github/workflows/sync-labels.yml
vendored
@@ -8,6 +8,9 @@ on:
|
|||||||
- develop
|
- develop
|
||||||
paths:
|
paths:
|
||||||
- .github/labels.yml
|
- .github/labels.yml
|
||||||
|
|
||||||
|
permissions: {} # We use ELEMENT_BOT_TOKEN instead
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
sync-labels:
|
sync-labels:
|
||||||
uses: element-hq/element-meta/.github/workflows/sync-labels.yml@develop
|
uses: element-hq/element-meta/.github/workflows/sync-labels.yml@develop
|
||||||
|
|||||||
10
.github/workflows/tests.yml
vendored
10
.github/workflows/tests.yml
vendored
@@ -26,10 +26,12 @@ env:
|
|||||||
# fetchdep.sh needs to know our PR number
|
# fetchdep.sh needs to know our PR number
|
||||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||||
|
|
||||||
|
permissions: {}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
jest:
|
jest:
|
||||||
name: Jest
|
name: Jest
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@@ -93,14 +95,16 @@ jobs:
|
|||||||
name: jest-tests
|
name: jest-tests
|
||||||
needs: jest
|
needs: jest
|
||||||
if: always()
|
if: always()
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
|
permissions:
|
||||||
|
statuses: write
|
||||||
steps:
|
steps:
|
||||||
- if: needs.jest.result != 'skipped' && needs.jest.result != 'success'
|
- if: needs.jest.result != 'skipped' && needs.jest.result != 'success'
|
||||||
run: exit 1
|
run: exit 1
|
||||||
|
|
||||||
- name: Skip SonarCloud in merge queue
|
- name: Skip SonarCloud in merge queue
|
||||||
if: github.event_name == 'merge_group' || inputs.disable_coverage == 'true'
|
if: github.event_name == 'merge_group' || inputs.disable_coverage == 'true'
|
||||||
uses: Sibz/github-status-action@faaa4d96fecf273bd762985e0e7f9f933c774918 # v1
|
uses: guibranco/github-status-action-v2@ecd54a02cf761e85a8fb328fe937710fd4227cda
|
||||||
with:
|
with:
|
||||||
authToken: ${{ secrets.GITHUB_TOKEN }}
|
authToken: ${{ secrets.GITHUB_TOKEN }}
|
||||||
state: success
|
state: success
|
||||||
|
|||||||
2
.github/workflows/triage-assigned.yml
vendored
2
.github/workflows/triage-assigned.yml
vendored
@@ -4,6 +4,8 @@ on:
|
|||||||
issues:
|
issues:
|
||||||
types: [assigned]
|
types: [assigned]
|
||||||
|
|
||||||
|
permissions: {} # We use ELEMENT_BOT_TOKEN instead
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
web-app-team:
|
web-app-team:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
|
|||||||
2
.github/workflows/triage-incoming.yml
vendored
2
.github/workflows/triage-incoming.yml
vendored
@@ -4,6 +4,8 @@ on:
|
|||||||
issues:
|
issues:
|
||||||
types: [opened]
|
types: [opened]
|
||||||
|
|
||||||
|
permissions: {} # We use ELEMENT_BOT_TOKEN instead
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
automate-project-columns:
|
automate-project-columns:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
|
|||||||
2
.github/workflows/triage-labelled.yml
vendored
2
.github/workflows/triage-labelled.yml
vendored
@@ -8,6 +8,8 @@ on:
|
|||||||
ELEMENT_BOT_TOKEN:
|
ELEMENT_BOT_TOKEN:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
|
permissions: {} # We use ELEMENT_BOT_TOKEN instead
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
apply_Z-Labs_label:
|
apply_Z-Labs_label:
|
||||||
name: Add Z-Labs label for features behind labs flags
|
name: Add Z-Labs label for features behind labs flags
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ on:
|
|||||||
pull_request_target:
|
pull_request_target:
|
||||||
types: [review_requested]
|
types: [review_requested]
|
||||||
|
|
||||||
|
permissions: {} # Uses ELEMENT_BOT_TOKEN instead
|
||||||
jobs:
|
jobs:
|
||||||
add_design_pr_to_project:
|
add_design_pr_to_project:
|
||||||
name: Move PRs asking for design review to the design board
|
name: Move PRs asking for design review to the design board
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
name: Close stale flaky issues
|
name: Close stale flaky issues
|
||||||
on:
|
on:
|
||||||
|
workflow_dispatch: {}
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "30 1 * * *"
|
- cron: "30 1 * * *"
|
||||||
|
permissions: {}
|
||||||
jobs:
|
jobs:
|
||||||
close:
|
close:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
@@ -16,3 +18,4 @@ jobs:
|
|||||||
days-before-close: 0
|
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."
|
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"
|
exempt-issue-labels: "Z-Flaky-Test-Disabled"
|
||||||
|
operations-per-run: 100
|
||||||
|
|||||||
4
.github/workflows/triage-unlabelled.yml
vendored
4
.github/workflows/triage-unlabelled.yml
vendored
@@ -3,11 +3,13 @@ name: Move unlabelled from needs info columns to triaged
|
|||||||
on:
|
on:
|
||||||
issues:
|
issues:
|
||||||
types: [unlabeled]
|
types: [unlabeled]
|
||||||
|
permissions: {}
|
||||||
jobs:
|
jobs:
|
||||||
Move_Unabeled_Issue_On_Project_Board:
|
Move_Unabeled_Issue_On_Project_Board:
|
||||||
name: Move no longer X-Needs-Info issues to Triaged
|
name: Move no longer X-Needs-Info issues to Triaged
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
|
permissions:
|
||||||
|
repository-projects: read
|
||||||
if: >
|
if: >
|
||||||
${{
|
${{
|
||||||
!contains(github.event.issue.labels.*.name, 'X-Needs-Info') }}
|
!contains(github.event.issue.labels.*.name, 'X-Needs-Info') }}
|
||||||
|
|||||||
3
.github/workflows/update-jitsi.yml
vendored
3
.github/workflows/update-jitsi.yml
vendored
@@ -4,6 +4,7 @@ on:
|
|||||||
workflow_dispatch: {}
|
workflow_dispatch: {}
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "0 3 * * 0" # 3am every Sunday
|
- cron: "0 3 * * 0" # 3am every Sunday
|
||||||
|
permissions: {} # We use ELEMENT_BOT_TOKEN instead
|
||||||
jobs:
|
jobs:
|
||||||
update:
|
update:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
@@ -22,7 +23,7 @@ jobs:
|
|||||||
run: "yarn update:jitsi"
|
run: "yarn update:jitsi"
|
||||||
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7
|
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
branch: actions/jitsi-update
|
branch: actions/jitsi-update
|
||||||
|
|||||||
1
.github/workflows/update-topics.yaml
vendored
1
.github/workflows/update-topics.yaml
vendored
@@ -15,6 +15,7 @@ on:
|
|||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
concurrency: ${{ github.workflow }}
|
concurrency: ${{ github.workflow }}
|
||||||
|
permissions: {} # No permissions required
|
||||||
jobs:
|
jobs:
|
||||||
bot:
|
bot:
|
||||||
name: Release topic update
|
name: Release topic update
|
||||||
|
|||||||
@@ -2,6 +2,6 @@
|
|||||||
"*": "prettier --write",
|
"*": "prettier --write",
|
||||||
"src/**/*.(ts|tsx)": ["eslint --fix"],
|
"src/**/*.(ts|tsx)": ["eslint --fix"],
|
||||||
"scripts/**/*.(ts|tsx)": ["eslint --fix"],
|
"scripts/**/*.(ts|tsx)": ["eslint --fix"],
|
||||||
"module_system/**/*.(ts|tsx)": ["eslint --fix --config .eslintrc-module_system.js module_system"],
|
"module_system/**/*.(ts|tsx)": ["eslint --fix"],
|
||||||
"*.pcss": ["stylelint --fix"]
|
"*.pcss": ["stylelint --fix"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
extends: ["stylelint-config-standard"],
|
extends: ["stylelint-config-standard"],
|
||||||
customSyntax: require("postcss-scss"),
|
customSyntax: "postcss-scss",
|
||||||
plugins: ["stylelint-scss"],
|
plugins: ["stylelint-scss", "stylelint-value-no-unknown-custom-properties"],
|
||||||
rules: {
|
rules: {
|
||||||
"comment-empty-line-before": null,
|
"comment-empty-line-before": null,
|
||||||
"declaration-empty-line-before": null,
|
"declaration-empty-line-before": null,
|
||||||
@@ -46,5 +46,33 @@ module.exports = {
|
|||||||
"number-max-precision": null,
|
"number-max-precision": null,
|
||||||
"no-invalid-double-slash-comments": true,
|
"no-invalid-double-slash-comments": true,
|
||||||
"media-feature-range-notation": null,
|
"media-feature-range-notation": null,
|
||||||
|
"csstools/value-no-unknown-custom-properties": [
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
importFrom: [
|
||||||
|
{ from: "res/css/_common.pcss", type: "css" },
|
||||||
|
{ from: "res/themes/light/css/_light.pcss", type: "css" },
|
||||||
|
// Right now our styles share vars all over the place, this is not ideal but acceptable for now
|
||||||
|
{ from: "res/css/views/rooms/_EventTile.pcss", type: "css" },
|
||||||
|
{ from: "res/css/views/rooms/_IRCLayout.pcss", type: "css" },
|
||||||
|
{ from: "res/css/views/rooms/_EventBubbleTile.pcss", type: "css" },
|
||||||
|
{ from: "res/css/views/rooms/_ReadReceiptGroup.pcss", type: "css" },
|
||||||
|
{ from: "res/css/views/rooms/_EditMessageComposer.pcss", type: "css" },
|
||||||
|
{ from: "res/css/views/right_panel/_BaseCard.pcss", type: "css" },
|
||||||
|
{ from: "res/css/views/messages/_MessageTimestamp.pcss", type: "css" },
|
||||||
|
{ from: "res/css/views/messages/_EventTileBubble.pcss", type: "css" },
|
||||||
|
{ from: "res/css/views/messages/_MessageActionBar.pcss", type: "css" },
|
||||||
|
{ from: "res/css/views/voip/LegacyCallView/_LegacyCallViewButtons.pcss", type: "css" },
|
||||||
|
{ from: "res/css/views/elements/_ToggleSwitch.pcss", type: "css" },
|
||||||
|
{ from: "res/css/views/settings/tabs/_SettingsTab.pcss", type: "css" },
|
||||||
|
{ from: "res/css/structures/_RoomView.pcss", type: "css" },
|
||||||
|
// Compound vars
|
||||||
|
"node_modules/@vector-im/compound-design-tokens/assets/web/css/cpd-common-base.css",
|
||||||
|
"node_modules/@vector-im/compound-design-tokens/assets/web/css/cpd-common-semantic.css",
|
||||||
|
"node_modules/@vector-im/compound-design-tokens/assets/web/css/cpd-theme-light-base-mq.css",
|
||||||
|
"node_modules/@vector-im/compound-design-tokens/assets/web/css/cpd-theme-light-semantic-mq.css",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
176
CHANGELOG.md
176
CHANGELOG.md
@@ -1,3 +1,179 @@
|
|||||||
|
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.
|
||||||
|
|
||||||
|
## 🐛 Bug Fixes
|
||||||
|
|
||||||
|
* Upgrade matrix-sdk-crypto-wasm to 1.11.0 (https://github.com/matrix-org/matrix-js-sdk/pull/4593)
|
||||||
|
* Fix url preview display ([#28766](https://github.com/element-hq/element-web/pull/28766)).
|
||||||
|
|
||||||
|
|
||||||
|
Changes in [1.11.88](https://github.com/element-hq/element-web/releases/tag/v1.11.88) (2024-12-17)
|
||||||
|
==================================================================================================
|
||||||
|
## ✨ Features
|
||||||
|
|
||||||
|
* Allow trusted Element Call widget to send and receive media encryption key to-device messages ([#28316](https://github.com/element-hq/element-web/pull/28316)). Contributed by @hughns.
|
||||||
|
* increase ringing timeout from 10 seconds to 90 seconds ([#28630](https://github.com/element-hq/element-web/pull/28630)). Contributed by @fkwp.
|
||||||
|
* Add `Close` tooltip to dialog ([#28617](https://github.com/element-hq/element-web/pull/28617)). Contributed by @florianduros.
|
||||||
|
* New UX for Share dialog ([#28598](https://github.com/element-hq/element-web/pull/28598)). Contributed by @florianduros.
|
||||||
|
* Improve performance of RoomContext in RoomHeader ([#28574](https://github.com/element-hq/element-web/pull/28574)). Contributed by @t3chguy.
|
||||||
|
* Remove `Features.RustCrypto` flag ([#28582](https://github.com/element-hq/element-web/pull/28582)). Contributed by @florianduros.
|
||||||
|
* Add Modernizr warning when running in non-secure context ([#28581](https://github.com/element-hq/element-web/pull/28581)). Contributed by @t3chguy.
|
||||||
|
|
||||||
|
## 🐛 Bug Fixes
|
||||||
|
|
||||||
|
* Fix jumpy timeline when the pinned message banner is displayed ([#28654](https://github.com/element-hq/element-web/pull/28654)). Contributed by @florianduros.
|
||||||
|
* Fix font \& spaces in settings subsection ([#28631](https://github.com/element-hq/element-web/pull/28631)). Contributed by @florianduros.
|
||||||
|
* Remove manual device verification which is not supported by the new cryptography stack ([#28588](https://github.com/element-hq/element-web/pull/28588)). Contributed by @florianduros.
|
||||||
|
* Fix code block highlighting not working reliably with many code blocks ([#28613](https://github.com/element-hq/element-web/pull/28613)). Contributed by @t3chguy.
|
||||||
|
* Remove remaining reply fallbacks code ([#28610](https://github.com/element-hq/element-web/pull/28610)). Contributed by @t3chguy.
|
||||||
|
* Provide a way to activate GIFs via the keyboard for a11y ([#28611](https://github.com/element-hq/element-web/pull/28611)). Contributed by @t3chguy.
|
||||||
|
* Fix format bar position ([#28591](https://github.com/element-hq/element-web/pull/28591)). Contributed by @florianduros.
|
||||||
|
* Fix room taking long time to load ([#28579](https://github.com/element-hq/element-web/pull/28579)). Contributed by @florianduros.
|
||||||
|
* Show the correct shield status in tooltip for more conditions ([#28476](https://github.com/element-hq/element-web/pull/28476)). Contributed by @uhoreg.
|
||||||
|
|
||||||
|
|
||||||
|
Changes in [1.11.87](https://github.com/element-hq/element-web/releases/tag/v1.11.87) (2024-12-03)
|
||||||
|
==================================================================================================
|
||||||
|
## ✨ Features
|
||||||
|
|
||||||
|
* Send and respect MSC4230 is\_animated flag ([#28513](https://github.com/element-hq/element-web/pull/28513)). Contributed by @t3chguy.
|
||||||
|
* Display a warning when an unverified user's identity changes ([#28211](https://github.com/element-hq/element-web/pull/28211)). Contributed by @uhoreg.
|
||||||
|
* Swap out Twitter link for Mastodon on auth footer ([#28508](https://github.com/element-hq/element-web/pull/28508)). Contributed by @t3chguy.
|
||||||
|
* Consider `org.matrix.msc3417.call` as video room in create room dialog ([#28497](https://github.com/element-hq/element-web/pull/28497)). Contributed by @t3chguy.
|
||||||
|
* Standardise icons using Compound Design Tokens ([#28217](https://github.com/element-hq/element-web/pull/28217)). Contributed by @t3chguy.
|
||||||
|
* Start sending stable `m.marked_unread` events ([#28478](https://github.com/element-hq/element-web/pull/28478)). Contributed by @tulir.
|
||||||
|
* Upgrade to compound-design-tokens v2 ([#28471](https://github.com/element-hq/element-web/pull/28471)). Contributed by @t3chguy.
|
||||||
|
* Standardise icons using Compound Design Tokens ([#28286](https://github.com/element-hq/element-web/pull/28286)). Contributed by @t3chguy.
|
||||||
|
* Remove reply fallbacks as per merged MSC2781 ([#28406](https://github.com/element-hq/element-web/pull/28406)). Contributed by @t3chguy.
|
||||||
|
* Use React Suspense when rendering async modals ([#28386](https://github.com/element-hq/element-web/pull/28386)). Contributed by @t3chguy.
|
||||||
|
|
||||||
|
## 🐛 Bug Fixes
|
||||||
|
|
||||||
|
* Add spinner when room encryption is loading in room settings ([#28535](https://github.com/element-hq/element-web/pull/28535)). Contributed by @florianduros.
|
||||||
|
* Fix getOidcCallbackUrl for Element Desktop ([#28521](https://github.com/element-hq/element-web/pull/28521)). Contributed by @t3chguy.
|
||||||
|
* Filter out redacted poll votes to avoid crashing the Poll widget ([#28498](https://github.com/element-hq/element-web/pull/28498)). Contributed by @t3chguy.
|
||||||
|
* Fix force tab complete not working since switching to React 18 createRoot API ([#28505](https://github.com/element-hq/element-web/pull/28505)). Contributed by @t3chguy.
|
||||||
|
* Fix media captions in bubble layout ([#28480](https://github.com/element-hq/element-web/pull/28480)). Contributed by @tulir.
|
||||||
|
* Reset cross-signing before backup when resetting both ([#28402](https://github.com/element-hq/element-web/pull/28402)). Contributed by @uhoreg.
|
||||||
|
* Listen to events so that encryption icon updates when status changes ([#28407](https://github.com/element-hq/element-web/pull/28407)). Contributed by @uhoreg.
|
||||||
|
* Check that the file the user chose has a MIME type of `image/*` ([#28467](https://github.com/element-hq/element-web/pull/28467)). Contributed by @t3chguy.
|
||||||
|
* Fix download button size in message action bar ([#28472](https://github.com/element-hq/element-web/pull/28472)). Contributed by @t3chguy.
|
||||||
|
* Allow tab completing users in brackets ([#28460](https://github.com/element-hq/element-web/pull/28460)). Contributed by @t3chguy.
|
||||||
|
* Fix React 18 strict mode breaking spotlight dialog ([#28452](https://github.com/element-hq/element-web/pull/28452)). Contributed by @MidhunSureshR.
|
||||||
|
|
||||||
|
|
||||||
|
Changes in [1.11.86](https://github.com/element-hq/element-web/releases/tag/v1.11.86) (2024-11-19)
|
||||||
|
==================================================================================================
|
||||||
|
## ✨ Features
|
||||||
|
|
||||||
|
* Deduplicate icons using Compound Design Tokens ([#28419](https://github.com/element-hq/element-web/pull/28419)). Contributed by @t3chguy.
|
||||||
|
* Let widget driver send error details ([#28357](https://github.com/element-hq/element-web/pull/28357)). Contributed by @AndrewFerr.
|
||||||
|
* Deduplicate icons using Compound Design Tokens ([#28381](https://github.com/element-hq/element-web/pull/28381)). Contributed by @t3chguy.
|
||||||
|
* Auto approvoce `io.element.call.reaction` capability for element call widgets ([#28401](https://github.com/element-hq/element-web/pull/28401)). Contributed by @toger5.
|
||||||
|
* Show message type prefix in thread root \& reply previews ([#28361](https://github.com/element-hq/element-web/pull/28361)). Contributed by @t3chguy.
|
||||||
|
* Support sending encrypted to device messages from widgets ([#28315](https://github.com/element-hq/element-web/pull/28315)). Contributed by @hughns.
|
||||||
|
|
||||||
|
## 🐛 Bug Fixes
|
||||||
|
|
||||||
|
* Feed events to widgets as they are decrypted (even if out of order) ([#28376](https://github.com/element-hq/element-web/pull/28376)). Contributed by @robintown.
|
||||||
|
* Handle authenticated media when downloading from ImageView ([#28379](https://github.com/element-hq/element-web/pull/28379)). Contributed by @t3chguy.
|
||||||
|
* Ignore `m.3pid_changes` for Identity service 3PID changes ([#28375](https://github.com/element-hq/element-web/pull/28375)). Contributed by @t3chguy.
|
||||||
|
* Fix markdown escaping wrongly passing html through ([#28363](https://github.com/element-hq/element-web/pull/28363)). Contributed by @t3chguy.
|
||||||
|
* Remove "Upgrade your encryption" flow in `CreateSecretStorageDialog` ([#28290](https://github.com/element-hq/element-web/pull/28290)). Contributed by @florianduros.
|
||||||
|
|
||||||
|
|
||||||
|
Changes in [1.11.85](https://github.com/element-hq/element-web/releases/tag/v1.11.85) (2024-11-12)
|
||||||
|
==================================================================================================
|
||||||
|
# Security
|
||||||
|
- Fixes for [CVE-2024-51750](https://www.cve.org/CVERecord?id=CVE-2024-51750) / [GHSA-w36j-v56h-q9pc](https://github.com/element-hq/element-web/security/advisories/GHSA-w36j-v56h-q9pc)
|
||||||
|
- Fixes for [CVE-2024-51749](https://www.cve.org/CVERecord?id=CVE-2024-51749) / [GHSA-5486-384g-mcx2](https://github.com/element-hq/element-web/security/advisories/GHSA-5486-384g-mcx2)
|
||||||
|
- Update JS SDK with the fixes for [CVE-2024-50336](https://www.cve.org/CVERecord?id=CVE-2024-50336) / [GHSA-xvg8-m4x3-w6xr](https://github.com/matrix-org/matrix-js-sdk/security/advisories/GHSA-xvg8-m4x3-w6xr)
|
||||||
|
|
||||||
|
|
||||||
|
Changes in [1.11.84](https://github.com/element-hq/element-web/releases/tag/v1.11.84) (2024-11-05)
|
||||||
|
==================================================================================================
|
||||||
|
## ✨ Features
|
||||||
|
|
||||||
|
* Remove abandoned MSC3886, MSC3903, MSC3906 implementations ([#28274](https://github.com/element-hq/element-web/pull/28274)). Contributed by @t3chguy.
|
||||||
|
* Update to React 18 ([#24763](https://github.com/element-hq/element-web/pull/24763)). Contributed by @t3chguy.
|
||||||
|
* Deduplicate icons using Compound ([#28239](https://github.com/element-hq/element-web/pull/28239)). Contributed by @t3chguy.
|
||||||
|
* Replace legacy Tooltips with Compound tooltips ([#28231](https://github.com/element-hq/element-web/pull/28231)). Contributed by @t3chguy.
|
||||||
|
* Deduplicate icons using Compound Design Tokens ([#28219](https://github.com/element-hq/element-web/pull/28219)). Contributed by @t3chguy.
|
||||||
|
* Add reactions to html export ([#28210](https://github.com/element-hq/element-web/pull/28210)). Contributed by @langleyd.
|
||||||
|
* Remove feature\_dehydration ([#28173](https://github.com/element-hq/element-web/pull/28173)). Contributed by @florianduros.
|
||||||
|
|
||||||
|
## 🐛 Bug Fixes
|
||||||
|
|
||||||
|
* Remove upgrade encryption in `DeviceListener` and `SetupEncryptionToast` ([#28299](https://github.com/element-hq/element-web/pull/28299)). Contributed by @florianduros.
|
||||||
|
* Fix 'remove alias' button in room settings ([#28269](https://github.com/element-hq/element-web/pull/28269)). Contributed by @Dev-Gurjar.
|
||||||
|
* Add back unencrypted path in `StopGapWidgetDriver.sendToDevice` ([#28295](https://github.com/element-hq/element-web/pull/28295)). Contributed by @florianduros.
|
||||||
|
* Fix other devices not being decorated as such ([#28279](https://github.com/element-hq/element-web/pull/28279)). Contributed by @t3chguy.
|
||||||
|
* Fix pill contrast in invitation dialog ([#28250](https://github.com/element-hq/element-web/pull/28250)). Contributed by @florianduros.
|
||||||
|
* Close right panel chat when minimising maximised voip widget ([#28241](https://github.com/element-hq/element-web/pull/28241)). Contributed by @t3chguy.
|
||||||
|
* Fix develop changelog parsing ([#28232](https://github.com/element-hq/element-web/pull/28232)). Contributed by @t3chguy.
|
||||||
|
* Fix Ctrl+F shortcut not working with minimised room summary card ([#28223](https://github.com/element-hq/element-web/pull/28223)). Contributed by @t3chguy.
|
||||||
|
* Fix network dropdown missing checkbox \& aria-checked ([#28220](https://github.com/element-hq/element-web/pull/28220)). Contributed by @t3chguy.
|
||||||
|
|
||||||
|
|
||||||
|
Changes in [1.11.83](https://github.com/element-hq/element-web/releases/tag/v1.11.83) (2024-10-29)
|
||||||
|
==================================================================================================
|
||||||
|
## ✨ Features
|
||||||
|
|
||||||
|
* Enable Element Call by default on release instances ([#28314](https://github.com/element-hq/element-web/pull/28314)). Contributed by @t3chguy.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Changes in [1.11.82](https://github.com/element-hq/element-web/releases/tag/v1.11.82) (2024-10-22)
|
||||||
|
==================================================================================================
|
||||||
|
## ✨ Features
|
||||||
|
|
||||||
|
* Deduplicate more icons using Compound Design Tokens ([#132](https://github.com/element-hq/matrix-react-sdk/pull/132)). Contributed by @t3chguy.
|
||||||
|
* Always show link new device flow even if unsupported ([#147](https://github.com/element-hq/matrix-react-sdk/pull/147)). Contributed by @t3chguy.
|
||||||
|
* Update design of files list in right panel ([#144](https://github.com/element-hq/matrix-react-sdk/pull/144)). Contributed by @t3chguy.
|
||||||
|
* Remove feature\_dehydration ([#138](https://github.com/element-hq/matrix-react-sdk/pull/138)). Contributed by @florianduros.
|
||||||
|
* Upgrade emojibase-bindings and remove local handling of emoticon variations ([#127](https://github.com/element-hq/matrix-react-sdk/pull/127)). Contributed by @langleyd.
|
||||||
|
* Add support for rendering media captions ([#43](https://github.com/element-hq/matrix-react-sdk/pull/43)). Contributed by @tulir.
|
||||||
|
* Replace composer icons with Compound variants ([#123](https://github.com/element-hq/matrix-react-sdk/pull/123)). Contributed by @t3chguy.
|
||||||
|
* Tweak default right panel size to be 320px except for maximised widgets at 420px ([#110](https://github.com/element-hq/matrix-react-sdk/pull/110)). Contributed by @t3chguy.
|
||||||
|
* Add a pinned message badge under a pinned message ([#118](https://github.com/element-hq/matrix-react-sdk/pull/118)). Contributed by @florianduros.
|
||||||
|
* Ditch right panel tabs and re-add close button ([#99](https://github.com/element-hq/matrix-react-sdk/pull/99)). Contributed by @t3chguy.
|
||||||
|
* Force verification even for refreshed clients ([#44](https://github.com/element-hq/matrix-react-sdk/pull/44)). Contributed by @dbkr.
|
||||||
|
* Update emoji text, border and background colour in timeline ([#119](https://github.com/element-hq/matrix-react-sdk/pull/119)). Contributed by @florianduros.
|
||||||
|
* Disable ICE fallback based on well-known configuration ([#111](https://github.com/element-hq/matrix-react-sdk/pull/111)). Contributed by @t3chguy.
|
||||||
|
* Remove legacy room header and promote beta room header ([#105](https://github.com/element-hq/matrix-react-sdk/pull/105)). Contributed by @t3chguy.
|
||||||
|
* Respect `io.element.jitsi` `useFor1To1Calls` in well-known ([#112](https://github.com/element-hq/matrix-react-sdk/pull/112)). Contributed by @t3chguy.
|
||||||
|
* Use Compound close icon in favour of mishmash of x/close icons ([#108](https://github.com/element-hq/matrix-react-sdk/pull/108)). Contributed by @t3chguy.
|
||||||
|
|
||||||
|
## 🐛 Bug Fixes
|
||||||
|
|
||||||
|
* Correct typo in option documentation ([#28148](https://github.com/element-hq/element-web/pull/28148)). Contributed by @AndrewKvalheim.
|
||||||
|
* Revert #124 and #135 ([#139](https://github.com/element-hq/matrix-react-sdk/pull/139)). Contributed by @dbkr.
|
||||||
|
* Add aria-label to e2e icon ([#136](https://github.com/element-hq/matrix-react-sdk/pull/136)). Contributed by @florianduros.
|
||||||
|
* Fix bell icons on room list hover being black squares ([#135](https://github.com/element-hq/matrix-react-sdk/pull/135)). Contributed by @dbkr.
|
||||||
|
* Fix vertical overflow on the mobile register screen ([#137](https://github.com/element-hq/matrix-react-sdk/pull/137)). Contributed by @langleyd.
|
||||||
|
* Allow to unpin redacted event ([#98](https://github.com/element-hq/matrix-react-sdk/pull/98)). Contributed by @florianduros.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Changes in [1.11.81](https://github.com/element-hq/element-web/releases/tag/v1.11.81) (2024-10-15)
|
Changes in [1.11.81](https://github.com/element-hq/element-web/releases/tag/v1.11.81) (2024-10-15)
|
||||||
==================================================================================================
|
==================================================================================================
|
||||||
This release fixes High severity vulnerability CVE-2024-47771 / GHSA-963w-49j9-gxj6
|
This release fixes High severity vulnerability CVE-2024-47771 / GHSA-963w-49j9-gxj6
|
||||||
|
|||||||
27
Dockerfile
27
Dockerfile
@@ -1,20 +1,17 @@
|
|||||||
# Builder
|
# 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.
|
# Support custom branch of the js-sdk. This also helps us build images of element-web develop.
|
||||||
ARG USE_CUSTOM_SDKS=false
|
ARG USE_CUSTOM_SDKS=false
|
||||||
ARG JS_SDK_REPO="https://github.com/matrix-org/matrix-js-sdk.git"
|
ARG JS_SDK_REPO="https://github.com/matrix-org/matrix-js-sdk.git"
|
||||||
ARG JS_SDK_BRANCH="master"
|
ARG JS_SDK_BRANCH="master"
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y git dos2unix
|
|
||||||
|
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
|
|
||||||
COPY . /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 yarn --network-timeout=200000 install
|
||||||
|
RUN /src/scripts/docker-package.sh
|
||||||
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
|
|
||||||
|
|
||||||
# Copy the config now so that we don't create another layer in the app image
|
# 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
|
RUN cp /src/config.sample.json /src/webapp/config.json
|
||||||
@@ -24,8 +21,22 @@ FROM nginx:alpine-slim
|
|||||||
|
|
||||||
COPY --from=builder /src/webapp /app
|
COPY --from=builder /src/webapp /app
|
||||||
|
|
||||||
# Override default nginx config
|
# Override default nginx config. Templates in `/etc/nginx/templates` are passed
|
||||||
COPY /nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf
|
# 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 \
|
RUN rm -rf /usr/share/nginx/html \
|
||||||
&& ln -s /app /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
6
LICENSE-COMMERCIAL
Normal 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
|
||||||
15
README.md
15
README.md
@@ -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).
|
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.
|
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.
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
// Stub out FontManager for tests as it doesn't validate anything we don't already know given
|
|
||||||
// our fixed test environment and it requires the installation of node-canvas.
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
fixupColorFonts: () => Promise.resolve(),
|
|
||||||
};
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
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.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -17,6 +17,7 @@ class MockMap extends EventEmitter {
|
|||||||
setCenter = jest.fn();
|
setCenter = jest.fn();
|
||||||
setStyle = jest.fn();
|
setStyle = jest.fn();
|
||||||
fitBounds = jest.fn();
|
fitBounds = jest.fn();
|
||||||
|
remove = jest.fn();
|
||||||
}
|
}
|
||||||
const MockMapInstance = new MockMap();
|
const MockMapInstance = new MockMap();
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
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.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1 @@
|
|||||||
{
|
{}
|
||||||
"src/components/views/auth/AuthFooter.tsx": "src/components/views/auth/VectorAuthFooter.tsx",
|
|
||||||
"src/components/views/auth/AuthHeaderLogo.tsx": "src/components/views/auth/VectorAuthHeaderLogo.tsx",
|
|
||||||
"src/components/views/auth/AuthPage.tsx": "src/components/views/auth/VectorAuthPage.tsx"
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"description": "A glossy Matrix collaboration client for the web.",
|
"description": "A glossy Matrix collaboration client for the web.",
|
||||||
"repository": {
|
"repository": {
|
||||||
"url": "https://github.com/element-hq/element-web",
|
"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": {
|
"bugs": {
|
||||||
"list": "https://github.com/element-hq/element-web/issues",
|
"list": "https://github.com/element-hq/element-web/issues",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
server {
|
server {
|
||||||
listen 80;
|
listen ${ELEMENT_WEB_PORT};
|
||||||
listen [::]:80;
|
listen [::]:${ELEMENT_WEB_PORT};
|
||||||
server_name localhost;
|
server_name localhost;
|
||||||
|
|
||||||
root /usr/share/nginx/html;
|
root /usr/share/nginx/html;
|
||||||
@@ -592,4 +592,3 @@ The following are undocumented or intended for developer use only.
|
|||||||
2. `sync_timeline_limit`
|
2. `sync_timeline_limit`
|
||||||
3. `dangerously_allow_unsafe_and_insecure_passwords`
|
3. `dangerously_allow_unsafe_and_insecure_passwords`
|
||||||
4. `latex_maths_delims`: An optional setting to override the default delimiters used for maths parsing. See https://github.com/matrix-org/matrix-react-sdk/pull/5939 for details. Only used when `feature_latex_maths` is enabled.
|
4. `latex_maths_delims`: An optional setting to override the default delimiters used for maths parsing. See https://github.com/matrix-org/matrix-react-sdk/pull/5939 for details. Only used when `feature_latex_maths` is enabled.
|
||||||
5. `voice_broadcast.chunk_length`: Target chunk length in seconds for the Voice Broadcast feature currently under development.
|
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ Customisations will be removed from the codebase in a future release.
|
|||||||
Element Web and the React SDK support "customisation points" that can be used to
|
Element Web and the React SDK support "customisation points" that can be used to
|
||||||
easily add custom logic specific to a particular deployment of Element Web.
|
easily add custom logic specific to a particular deployment of Element Web.
|
||||||
|
|
||||||
An example of this is the [security customisations
|
An example of this is the [media customisations
|
||||||
module](https://github.com/element-hq/element-web/blob/develop/src/customisations/Security.ts).
|
module](https://github.com/element-hq/element-web/blob/develop/src/customisations/Media.ts).
|
||||||
This module in the React SDK only defines some empty functions and their types:
|
This module in the React SDK only defines some empty functions and their types:
|
||||||
it does not do anything by default.
|
it does not do anything by default.
|
||||||
|
|
||||||
@@ -21,14 +21,14 @@ Web so that you can add your own code. Even though the default module is part of
|
|||||||
the React SDK, you can still override it from the Element Web layer:
|
the React SDK, you can still override it from the Element Web layer:
|
||||||
|
|
||||||
1. Copy the default customisation module to
|
1. Copy the default customisation module to
|
||||||
`element-web/src/customisations/YourNameSecurity.ts`
|
`element-web/src/customisations/YourNameMedia.ts`
|
||||||
2. Edit customisations points and make sure export the ones you actually want to
|
2. Edit customisations points and make sure export the ones you actually want to
|
||||||
activate
|
activate
|
||||||
3. Create/add an entry to `customisations.json` next to the webpack config:
|
3. Create/add an entry to `customisations.json` next to the webpack config:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"src/customisations/Security.ts": "src/customisations/YourNameSecurity.ts"
|
"src/customisations/Media.ts": "src/customisations/YourNameMedia.ts"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,15 @@ The Docker image can be used to serve element-web as a web server. The easiest w
|
|||||||
it is to use the prebuilt image:
|
it is to use the prebuilt image:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -p 80:80 vectorim/element-web
|
docker run --rm -p 127.0.0.1:80:80 vectorim/element-web
|
||||||
|
```
|
||||||
|
|
||||||
|
A server can also be made available to clients outside the local host by omitting the
|
||||||
|
explicit local address as described in
|
||||||
|
[docker run documentation](https://docs.docker.com/engine/reference/commandline/run/#publish-or-expose-port--p---expose):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run --rm -p 80:80 vectorim/element-web
|
||||||
```
|
```
|
||||||
|
|
||||||
To supply your own custom `config.json`, map a volume to `/app/config.json`. For example,
|
To supply your own custom `config.json`, map a volume to `/app/config.json`. For example,
|
||||||
@@ -49,9 +57,25 @@ if your custom config was located at `/etc/element-web/config.json` then your Do
|
|||||||
would be:
|
would be:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -p 80:80 -v /etc/element-web/config.json:/app/config.json vectorim/element-web
|
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:
|
To build the image yourself:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
22
docs/oidc.md
22
docs/oidc.md
@@ -1,29 +1,9 @@
|
|||||||
# OIDC and delegated authentication
|
# 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.
|
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.
|
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.
|
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/).
|
Then, authentication will be completed [as described here](https://areweoidcyet.com/client-implementation-guide/).
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
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.
|
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
|
The tests use [testcontainers](https://node.testcontainers.org/) to launch Homeserver (Synapse or Dendrite)
|
||||||
need to have Docker installed and working in order to run the Playwright tests.
|
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:
|
There are a few different ways to run the tests yourself. The simplest is to run:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
docker pull ghcr.io/element-hq/synapse:develop
|
|
||||||
yarn run test:playwright
|
yarn run test:playwright
|
||||||
```
|
```
|
||||||
|
|
||||||
This will run the Playwright tests once, non-interactively.
|
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:
|
You can also run individual tests this way too, as you'd expect:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
@@ -53,41 +51,36 @@ yarn run test:playwright:open --headed --debug
|
|||||||
|
|
||||||
See more command line options at <https://playwright.dev/docs/test-cli>.
|
See more command line options at <https://playwright.dev/docs/test-cli>.
|
||||||
|
|
||||||
### Running with Rust cryptography
|
## Projects
|
||||||
|
|
||||||
`matrix-js-sdk` is currently in the
|
By default, Playwright will run all "Projects", this means tests will run against Chrome, Firefox and "Safari" (Webkit).
|
||||||
[process](https://github.com/vector-im/element-web/issues/21972) of being
|
We only run tests against Chrome in pull request CI, but all projects in the merge queue.
|
||||||
updated to replace its end-to-end encryption implementation to use the [Matrix
|
Some tests are excluded from running on certain browsers due to incompatibilities in the test harness.
|
||||||
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.
|
|
||||||
|
|
||||||
## How the Tests Work
|
## 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`.
|
as is typical for Playwright tests. Likewise, tests live in `playwright/e2e`.
|
||||||
|
|
||||||
`playwright/plugins/homeservers` contains Playwright plugins that starts instances
|
`playwright/testcontainers` contains the testcontainers which start instances
|
||||||
of Synapse/Dendrite in Docker containers. These servers are what Element-web runs
|
of Synapse/Dendrite. These servers are what Element-web runs against in the tests.
|
||||||
against in the tests.
|
|
||||||
|
|
||||||
Synapse can be launched with different configurations in order to test element
|
Synapse can be launched with different configurations in order to test element
|
||||||
in different configurations. `playwright/plugins/homeserver/synapse/templates`
|
in different configurations. You can specify `synapseConfig` as such:
|
||||||
contains template configuration files for each different configuration.
|
|
||||||
|
|
||||||
Each test suite can then launch whatever Synapse instances it needs in whatever
|
```typescript
|
||||||
configurations.
|
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
|
The appropriate homeserver will be launched by the Playwright worker and reused for all tests which match the worker configuration.
|
||||||
plugin also stop any remaining instances after all tests have run, it is possible
|
Due to homeservers being reused between tests, please use unique names for any rooms put into the room directory as
|
||||||
to be left with some stray containers if, for example, you terminate a test such
|
they may be visible from other tests, the suggested approach is to use `testInfo.testId` within the name or lodash's uniqueId.
|
||||||
that the `after()` does not run and also exit Playwright uncleanly. All the containers
|
We remove public rooms from the room directory between tests but deleting users doesn't have a homeserver agnostic solution.
|
||||||
it starts are prefixed, so they are easy to recognise. They can be removed safely.
|
The logs from testcontainers will be attached to any reports output from Playwright.
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
## Writing Tests
|
## 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).
|
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.
|
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
|
### Logging In
|
||||||
|
|
||||||
We again heavily leverage the magic of [Playwright fixtures](https://playwright.dev/docs/test-fixtures).
|
We again heavily leverage the magic of [Playwright fixtures](https://playwright.dev/docs/test-fixtures).
|
||||||
@@ -217,3 +191,27 @@ instead of the native `toHaveScreenshot`.
|
|||||||
|
|
||||||
If you are running Linux and are unfortunate that the screenshots are not rendering identically,
|
If you are running Linux and are unfortunate that the screenshots are not rendering identically,
|
||||||
you may wish to specify `--ignore-snapshots` and rely on Docker to render them for you.
|
you may wish to specify `--ignore-snapshots` and rely on Docker to render them for you.
|
||||||
|
|
||||||
|
## Test Tags
|
||||||
|
|
||||||
|
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.
|
||||||
|
|||||||
@@ -8,11 +8,13 @@
|
|||||||
|
|
||||||
#### develop
|
#### 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
|
#### 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.
|
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
|
#### 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.
|
We ship Element Desktop to packages.element.io.
|
||||||
|
|
||||||
- [ ] Check that element-web has shipped to dockerhub
|
- [ ] 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
|
- [ ] Test staging.element.io
|
||||||
|
|
||||||
For final releases additionally do these steps:
|
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 Web package has shipped to packages.element.io
|
||||||
- [ ] Ensure Element Desktop packages have 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
|
# Housekeeping
|
||||||
|
|
||||||
We have some manual housekeeping to do in order to prepare for the next release.
|
We have some manual housekeeping to do in order to prepare for the next release.
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ default theme, you would use `default_theme: "custom-Electric Blue"`.
|
|||||||
|
|
||||||
e.g. in config.json:
|
e.g. in config.json:
|
||||||
|
|
||||||
```
|
```json5
|
||||||
"setting_defaults": {
|
"setting_defaults": {
|
||||||
"custom_themes": [
|
"custom_themes": [
|
||||||
{
|
{
|
||||||
@@ -59,6 +59,10 @@ e.g. in config.json:
|
|||||||
"timeline-text-color": "#2e2f32",
|
"timeline-text-color": "#2e2f32",
|
||||||
"timeline-text-secondary-color": "#61708b",
|
"timeline-text-secondary-color": "#61708b",
|
||||||
"timeline-highlights-color": "#f3f8fd",
|
"timeline-highlights-color": "#f3f8fd",
|
||||||
|
|
||||||
|
// These should both be 8 values long
|
||||||
|
"username-colors": ["#ff0000", /*...*/],
|
||||||
|
"avatar-background-colors": ["#cc0000", /*...*/]
|
||||||
},
|
},
|
||||||
"compound": {
|
"compound": {
|
||||||
"--cpd-color-icon-accent-tertiary": "var(--cpd-color-blue-800)",
|
"--cpd-color-icon-accent-tertiary": "var(--cpd-color-blue-800)",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
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.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -14,6 +14,8 @@ const config: Config = {
|
|||||||
testEnvironment: "jsdom",
|
testEnvironment: "jsdom",
|
||||||
testEnvironmentOptions: {
|
testEnvironmentOptions: {
|
||||||
url: "http://localhost/",
|
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)"],
|
testMatch: ["<rootDir>/test/**/*-test.[tj]s?(x)"],
|
||||||
globalSetup: "<rootDir>/test/globalSetup.ts",
|
globalSetup: "<rootDir>/test/globalSetup.ts",
|
||||||
@@ -32,13 +34,12 @@ const config: Config = {
|
|||||||
"decoderWorker\\.min\\.wasm": "<rootDir>/__mocks__/empty.js",
|
"decoderWorker\\.min\\.wasm": "<rootDir>/__mocks__/empty.js",
|
||||||
"waveWorker\\.min\\.js": "<rootDir>/__mocks__/empty.js",
|
"waveWorker\\.min\\.js": "<rootDir>/__mocks__/empty.js",
|
||||||
"context-filter-polyfill": "<rootDir>/__mocks__/empty.js",
|
"context-filter-polyfill": "<rootDir>/__mocks__/empty.js",
|
||||||
"FontManager.ts": "<rootDir>/__mocks__/FontManager.js",
|
|
||||||
"workers/(.+)Factory": "<rootDir>/__mocks__/workerFactoryMock.js",
|
"workers/(.+)Factory": "<rootDir>/__mocks__/workerFactoryMock.js",
|
||||||
"^!!raw-loader!.*": "jest-raw-loader",
|
"^!!raw-loader!.*": "jest-raw-loader",
|
||||||
"recorderWorkletFactory": "<rootDir>/__mocks__/empty.js",
|
"recorderWorkletFactory": "<rootDir>/__mocks__/empty.js",
|
||||||
"^fetch-mock$": "<rootDir>/node_modules/fetch-mock",
|
"^fetch-mock$": "<rootDir>/node_modules/fetch-mock",
|
||||||
},
|
},
|
||||||
transformIgnorePatterns: ["/node_modules/(?!matrix-js-sdk).+$"],
|
transformIgnorePatterns: ["/node_modules/(?!(mime|matrix-js-sdk)).+$"],
|
||||||
collectCoverageFrom: [
|
collectCoverageFrom: [
|
||||||
"<rootDir>/src/**/*.{js,ts,tsx}",
|
"<rootDir>/src/**/*.{js,ts,tsx}",
|
||||||
// getSessionLock is piped into a different JS context via stringification, and the coverage functionality is
|
// getSessionLock is piped into a different JS context via stringification, and the coverage functionality is
|
||||||
|
|||||||
48
knip.ts
Normal file
48
knip.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { KnipConfig } from "knip";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
entry: [
|
||||||
|
"src/vector/index.ts",
|
||||||
|
"src/serviceworker/index.ts",
|
||||||
|
"src/workers/*.worker.ts",
|
||||||
|
"src/utils/exportUtils/exportJS.js",
|
||||||
|
"scripts/**",
|
||||||
|
"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: [
|
||||||
|
// Keep for now
|
||||||
|
"src/hooks/useLocalStorageState.ts",
|
||||||
|
"src/components/views/elements/InfoTooltip.tsx",
|
||||||
|
"src/components/views/elements/StyledCheckbox.tsx",
|
||||||
|
],
|
||||||
|
ignoreDependencies: [
|
||||||
|
// Required for `action-validator`
|
||||||
|
"@action-validator/*",
|
||||||
|
// Used for git pre-commit hooks
|
||||||
|
"husky",
|
||||||
|
// Used by jest
|
||||||
|
"babel-jest",
|
||||||
|
// Used by babel
|
||||||
|
"@babel/runtime",
|
||||||
|
"@babel/plugin-transform-class-properties",
|
||||||
|
// Referenced in PCSS
|
||||||
|
"github-markdown-css",
|
||||||
|
// False positive
|
||||||
|
"sw.js",
|
||||||
|
// Used by webpack
|
||||||
|
"process",
|
||||||
|
"util",
|
||||||
|
],
|
||||||
|
ignoreBinaries: [
|
||||||
|
// Used in scripts & workflows
|
||||||
|
"jq",
|
||||||
|
],
|
||||||
|
ignoreExportsUsedInFile: true,
|
||||||
|
} satisfies KnipConfig;
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2022-2024 New Vector Ltd.
|
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.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2022-2024 New Vector Ltd.
|
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.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2022-2024 New Vector Ltd.
|
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.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
108
package.json
108
package.json
@@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"name": "element-web",
|
"name": "element-web",
|
||||||
"version": "1.11.81",
|
"version": "1.11.90",
|
||||||
"description": "A feature-rich client for Matrix.org",
|
"description": "A feature-rich client for Matrix.org",
|
||||||
"author": "New Vector Ltd.",
|
"author": "New Vector Ltd.",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/element-hq/element-web"
|
"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": [
|
"files": [
|
||||||
"lib",
|
"lib",
|
||||||
"res",
|
"res",
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
"i18n:lint": "matrix-i18n-lint && prettier --log-level=silent --write src/i18n/strings/ --ignore-path /dev/null",
|
"i18n:lint": "matrix-i18n-lint && prettier --log-level=silent --write src/i18n/strings/ --ignore-path /dev/null",
|
||||||
"i18n:diff": "cp src/i18n/strings/en_EN.json src/i18n/strings/en_EN_orig.json && yarn i18n && matrix-compare-i18n-files src/i18n/strings/en_EN_orig.json src/i18n/strings/en_EN.json",
|
"i18n:diff": "cp src/i18n/strings/en_EN.json src/i18n/strings/en_EN_orig.json && yarn i18n && matrix-compare-i18n-files src/i18n/strings/en_EN_orig.json src/i18n/strings/en_EN.json",
|
||||||
"make-component": "node scripts/make-react-component.js",
|
"make-component": "node scripts/make-react-component.js",
|
||||||
"rethemendex": "res/css/rethemendex.sh",
|
"rethemendex": "./res/css/rethemendex.sh",
|
||||||
"clean": "rimraf lib webapp",
|
"clean": "rimraf lib webapp",
|
||||||
"build": "yarn clean && yarn build:genfiles && yarn build:bundle",
|
"build": "yarn clean && yarn build:genfiles && yarn build:bundle",
|
||||||
"build-stats": "yarn clean && yarn build:genfiles && yarn build:bundle-stats",
|
"build-stats": "yarn clean && yarn build:genfiles && yarn build:bundle-stats",
|
||||||
@@ -45,58 +45,59 @@
|
|||||||
"build:bundle": "webpack --progress --mode production",
|
"build:bundle": "webpack --progress --mode production",
|
||||||
"build:bundle-stats": "webpack --progress --mode production --json > webpack-stats.json",
|
"build:bundle-stats": "webpack --progress --mode production --json > webpack-stats.json",
|
||||||
"build:module_system": "ts-node --project ./tsconfig.module_system.json module_system/scripts/install.ts",
|
"build:module_system": "ts-node --project ./tsconfig.module_system.json module_system/scripts/install.ts",
|
||||||
"dist": "scripts/package.sh",
|
"dist": "./scripts/package.sh",
|
||||||
"start": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n modules,res \"yarn build:module_system\" \"yarn build:res\" && concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n res,element-js \"yarn start:res\" \"yarn start:js\"",
|
"start": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n modules,res \"yarn build:module_system\" \"yarn build:res\" && concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n res,element-js \"yarn start:res\" \"yarn start:js\"",
|
||||||
"start:https": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n res,element-js \"yarn start:res\" \"yarn start:js --server-type https\"",
|
"start:https": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n res,element-js \"yarn start:res\" \"yarn start:js --server-type https\"",
|
||||||
"start:res": "ts-node scripts/copy-res.ts -w",
|
"start:res": "ts-node scripts/copy-res.ts -w",
|
||||||
"start:js": "webpack serve --output-path webapp --output-filename=bundles/_dev_/[name].js --output-chunk-filename=bundles/_dev_/[name].js --mode development",
|
"start:js": "webpack serve --output-path webapp --output-filename=bundles/_dev_/[name].js --output-chunk-filename=bundles/_dev_/[name].js --mode development",
|
||||||
"lint": "yarn lint:types && yarn lint:js && yarn lint:style && yarn lint:workflows",
|
"lint": "yarn lint:types && yarn lint:js && yarn lint:style && yarn lint:workflows",
|
||||||
"lint:js": "yarn lint:js:src && yarn lint:js:module_system",
|
"lint:js": "eslint --max-warnings 0 src test playwright module_system && prettier --check .",
|
||||||
"lint:js:src": "eslint --max-warnings 0 src test playwright && prettier --check .",
|
"lint:js-fix": "prettier --log-level=warn --write . && eslint --fix src test playwright module_system",
|
||||||
"lint:js:module_system": "eslint --max-warnings 0 --config .eslintrc-module_system.js module_system",
|
|
||||||
"lint:js-fix": "yarn lint:js-fix:src && yarn lint:js-fix:module_system",
|
|
||||||
"lint:js-fix:src": "prettier --log-level=warn --write . && eslint --fix src test playwright",
|
|
||||||
"lint:js-fix:module_system": "eslint --fix --config .eslintrc-module_system.js module_system",
|
|
||||||
"lint:types": "yarn lint:types:src && yarn lint:types:module_system",
|
"lint:types": "yarn lint:types:src && yarn lint:types:module_system",
|
||||||
"lint:types:src": "tsc --noEmit --jsx react && tsc --noEmit --jsx react -p playwright",
|
"lint:types:src": "tsc --noEmit --jsx react && tsc --noEmit --jsx react -p playwright",
|
||||||
"lint:types:module_system": "tsc --noEmit --project ./tsconfig.module_system.json",
|
"lint:types:module_system": "tsc --noEmit --project ./tsconfig.module_system.json",
|
||||||
"lint:style": "stylelint \"res/css/**/*.pcss\"",
|
"lint:style": "stylelint \"res/css/**/*.pcss\"",
|
||||||
"lint:workflows": "find .github/workflows -type f \\( -iname '*.yaml' -o -iname '*.yml' \\) | xargs -I {} sh -c 'echo \"Linting {}\"; action-validator \"{}\"'",
|
"lint:workflows": "find .github/workflows -type f \\( -iname '*.yaml' -o -iname '*.yml' \\) | xargs -I {} sh -c 'echo \"Linting {}\"; action-validator \"{}\"'",
|
||||||
|
"lint:knip": "knip",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:playwright": "playwright test",
|
"test:playwright": "playwright test",
|
||||||
"test:playwright:open": "yarn test:playwright --ui",
|
"test:playwright:open": "yarn test:playwright --ui",
|
||||||
"test:playwright:screenshots": "yarn test:playwright:screenshots:build && yarn test:playwright:screenshots:run",
|
"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: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",
|
"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",
|
"coverage": "yarn test --coverage",
|
||||||
"analyse:unused-exports": "ts-node ./scripts/analyse_unused_exports.ts",
|
|
||||||
"analyse:webpack-bundles": "webpack-bundle-analyzer webpack-stats.json webapp",
|
"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"
|
"update:jitsi": "curl -s https://meet.element.io/libs/external_api.min.js > ./res/jitsi_external_api.min.js"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"@types/seedrandom": "3.0.8",
|
"@types/react": "18.3.18",
|
||||||
|
"@types/react-dom": "18.3.5",
|
||||||
"oidc-client-ts": "3.1.0",
|
"oidc-client-ts": "3.1.0",
|
||||||
"jwt-decode": "4.0.0",
|
"jwt-decode": "4.0.0",
|
||||||
"caniuse-lite": "1.0.30001668",
|
"caniuse-lite": "1.0.30001692",
|
||||||
"wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0",
|
"wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0",
|
||||||
"wrap-ansi": "npm:wrap-ansi@^7.0.0"
|
"wrap-ansi": "npm:wrap-ansi@^7.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.12.5",
|
"@babel/runtime": "^7.12.5",
|
||||||
|
"@fontsource/inconsolata": "^5",
|
||||||
|
"@fontsource/inter": "^5",
|
||||||
"@formatjs/intl-segmenter": "^11.5.7",
|
"@formatjs/intl-segmenter": "^11.5.7",
|
||||||
"@matrix-org/analytics-events": "^0.28.0",
|
"@matrix-org/analytics-events": "^0.29.0",
|
||||||
"@matrix-org/emojibase-bindings": "^1.3.3",
|
"@matrix-org/emojibase-bindings": "^1.3.3",
|
||||||
"@vector-im/matrix-wysiwyg": "2.37.13",
|
|
||||||
"@matrix-org/react-sdk-module-api": "^2.4.0",
|
"@matrix-org/react-sdk-module-api": "^2.4.0",
|
||||||
"@matrix-org/spec": "^1.7.0",
|
"@matrix-org/spec": "^1.7.0",
|
||||||
"@sentry/browser": "^8.0.0",
|
"@sentry/browser": "^8.0.0",
|
||||||
"@vector-im/compound-design-tokens": "^1.8.0",
|
"@types/png-chunks-extract": "^1.0.2",
|
||||||
"@vector-im/compound-web": "^7.1.0",
|
"@types/react-virtualized": "^9.21.30",
|
||||||
|
"@vector-im/compound-design-tokens": "^2.1.0",
|
||||||
|
"@vector-im/compound-web": "^7.5.0",
|
||||||
|
"@vector-im/matrix-wysiwyg": "2.38.0",
|
||||||
"@zxcvbn-ts/core": "^3.0.4",
|
"@zxcvbn-ts/core": "^3.0.4",
|
||||||
"@zxcvbn-ts/language-common": "^3.0.4",
|
"@zxcvbn-ts/language-common": "^3.0.4",
|
||||||
"@zxcvbn-ts/language-en": "^3.0.2",
|
"@zxcvbn-ts/language-en": "^3.0.2",
|
||||||
"await-lock": "^2.1.0",
|
"await-lock": "^2.1.0",
|
||||||
"bloom-filters": "^3.0.1",
|
"bloom-filters": "^3.0.3",
|
||||||
"blurhash": "^2.0.3",
|
"blurhash": "^2.0.3",
|
||||||
"browserslist": "^4.23.2",
|
"browserslist": "^4.23.2",
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
@@ -114,37 +115,39 @@
|
|||||||
"highlight.js": "^11.3.1",
|
"highlight.js": "^11.3.1",
|
||||||
"html-entities": "^2.0.0",
|
"html-entities": "^2.0.0",
|
||||||
"is-ip": "^3.1.0",
|
"is-ip": "^3.1.0",
|
||||||
"jsrsasign": "^11.0.0",
|
|
||||||
"js-xxhash": "^4.0.0",
|
"js-xxhash": "^4.0.0",
|
||||||
|
"jsrsasign": "^11.0.0",
|
||||||
"jszip": "^3.7.0",
|
"jszip": "^3.7.0",
|
||||||
"katex": "^0.16.0",
|
"katex": "^0.16.0",
|
||||||
"linkify-element": "4.1.3",
|
"linkify-element": "4.2.0",
|
||||||
"linkify-react": "4.1.3",
|
"linkify-react": "4.2.0",
|
||||||
"linkify-string": "4.1.3",
|
"linkify-string": "4.2.0",
|
||||||
"linkifyjs": "4.1.3",
|
"linkifyjs": "4.2.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"maplibre-gl": "^2.0.0",
|
"maplibre-gl": "^5.0.0",
|
||||||
"matrix-encrypt-attachment": "^1.0.3",
|
"matrix-encrypt-attachment": "^1.0.3",
|
||||||
"matrix-events-sdk": "0.0.1",
|
"matrix-events-sdk": "0.0.1",
|
||||||
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
|
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
|
||||||
"matrix-widget-api": "^1.9.0",
|
"matrix-widget-api": "1.11.0",
|
||||||
"memoize-one": "^6.0.0",
|
"memoize-one": "^6.0.0",
|
||||||
|
"mime": "^4.0.4",
|
||||||
"oidc-client-ts": "^3.0.1",
|
"oidc-client-ts": "^3.0.1",
|
||||||
"opus-recorder": "^8.0.3",
|
"opus-recorder": "^8.0.3",
|
||||||
"pako": "^2.0.3",
|
"pako": "^2.0.3",
|
||||||
"png-chunks-extract": "^1.0.0",
|
"png-chunks-extract": "^1.0.0",
|
||||||
"posthog-js": "1.157.2",
|
"posthog-js": "1.157.2",
|
||||||
"qrcode": "1.5.4",
|
"qrcode": "1.5.4",
|
||||||
"re-resizable": "6.9.17",
|
"re-resizable": "6.10.3",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-beautiful-dnd": "^13.1.0",
|
"react-beautiful-dnd": "^13.1.0",
|
||||||
"react-blurhash": "^0.3.0",
|
"react-blurhash": "^0.3.0",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-focus-lock": "^2.5.1",
|
"react-focus-lock": "^2.5.1",
|
||||||
"react-transition-group": "^4.4.1",
|
"react-transition-group": "^4.4.1",
|
||||||
|
"react-virtualized": "^9.22.5",
|
||||||
"rfc4648": "^1.4.0",
|
"rfc4648": "^1.4.0",
|
||||||
"sanitize-filename": "^1.6.3",
|
"sanitize-filename": "^1.6.3",
|
||||||
"sanitize-html": "2.13.1",
|
"sanitize-html": "2.14.0",
|
||||||
"tar-js": "^0.3.0",
|
"tar-js": "^0.3.0",
|
||||||
"temporal-polyfill": "^0.2.5",
|
"temporal-polyfill": "^0.2.5",
|
||||||
"ua-parser-js": "^1.0.2",
|
"ua-parser-js": "^1.0.2",
|
||||||
@@ -155,11 +158,9 @@
|
|||||||
"@action-validator/cli": "^0.6.0",
|
"@action-validator/cli": "^0.6.0",
|
||||||
"@action-validator/core": "^0.6.0",
|
"@action-validator/core": "^0.6.0",
|
||||||
"@axe-core/playwright": "^4.8.1",
|
"@axe-core/playwright": "^4.8.1",
|
||||||
"@babel/cli": "^7.12.10",
|
|
||||||
"@babel/core": "^7.12.10",
|
"@babel/core": "^7.12.10",
|
||||||
"@babel/eslint-parser": "^7.12.10",
|
"@babel/eslint-parser": "^7.12.10",
|
||||||
"@babel/eslint-plugin": "^7.12.10",
|
"@babel/eslint-plugin": "^7.12.10",
|
||||||
"@babel/parser": "^7.12.11",
|
|
||||||
"@babel/plugin-proposal-export-default-from": "^7.12.1",
|
"@babel/plugin-proposal-export-default-from": "^7.12.1",
|
||||||
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||||
"@babel/plugin-transform-class-properties": "^7.12.1",
|
"@babel/plugin-transform-class-properties": "^7.12.1",
|
||||||
@@ -172,28 +173,26 @@
|
|||||||
"@babel/preset-env": "^7.12.11",
|
"@babel/preset-env": "^7.12.11",
|
||||||
"@babel/preset-react": "^7.12.10",
|
"@babel/preset-react": "^7.12.10",
|
||||||
"@babel/preset-typescript": "^7.12.7",
|
"@babel/preset-typescript": "^7.12.7",
|
||||||
"@babel/register": "^7.12.10",
|
|
||||||
"@babel/runtime": "^7.12.5",
|
"@babel/runtime": "^7.12.5",
|
||||||
"@casualbot/jest-sonar-reporter": "2.2.7",
|
"@casualbot/jest-sonar-reporter": "2.2.7",
|
||||||
"@peculiar/webcrypto": "^1.4.3",
|
"@peculiar/webcrypto": "^1.4.3",
|
||||||
"@playwright/test": "^1.40.1",
|
"@playwright/test": "^1.40.1",
|
||||||
"@principalstudio/html-webpack-inject-preload": "^1.2.7",
|
"@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",
|
"@stylistic/eslint-plugin": "^2.9.0",
|
||||||
"@svgr/webpack": "^8.0.0",
|
"@svgr/webpack": "^8.0.0",
|
||||||
|
"@testcontainers/postgresql": "^10.16.0",
|
||||||
"@testing-library/dom": "^10.4.0",
|
"@testing-library/dom": "^10.4.0",
|
||||||
"@testing-library/jest-dom": "^6.4.8",
|
"@testing-library/jest-dom": "^6.4.8",
|
||||||
"@testing-library/react": "^16.0.0",
|
"@testing-library/react": "^16.0.0",
|
||||||
"@testing-library/user-event": "^14.5.2",
|
"@testing-library/user-event": "^14.5.2",
|
||||||
"@types/commonmark": "^0.27.4",
|
"@types/commonmark": "^0.27.4",
|
||||||
"@types/content-type": "^1.1.5",
|
|
||||||
"@types/counterpart": "^0.18.1",
|
"@types/counterpart": "^0.18.1",
|
||||||
"@types/css-tree": "^2.3.8",
|
"@types/css-tree": "^2.3.8",
|
||||||
"@types/diff-match-patch": "^1.0.32",
|
"@types/diff-match-patch": "^1.0.32",
|
||||||
"@types/escape-html": "^1.0.1",
|
"@types/escape-html": "^1.0.1",
|
||||||
"@types/express": "^5.0.0",
|
"@types/express": "^5.0.0",
|
||||||
"@types/file-saver": "^2.0.3",
|
"@types/file-saver": "^2.0.3",
|
||||||
"@types/fs-extra": "^11.0.0",
|
|
||||||
"@types/glob-to-regexp": "^0.4.1",
|
"@types/glob-to-regexp": "^0.4.1",
|
||||||
"@types/jest": "29.5.12",
|
"@types/jest": "29.5.12",
|
||||||
"@types/jitsi-meet": "^2.0.2",
|
"@types/jitsi-meet": "^2.0.2",
|
||||||
@@ -203,27 +202,24 @@
|
|||||||
"@types/minimist": "^1.2.5",
|
"@types/minimist": "^1.2.5",
|
||||||
"@types/modernizr": "^3.5.3",
|
"@types/modernizr": "^3.5.3",
|
||||||
"@types/node": "18",
|
"@types/node": "18",
|
||||||
|
"@types/node-fetch": "^2.6.2",
|
||||||
"@types/pako": "^2.0.0",
|
"@types/pako": "^2.0.0",
|
||||||
"@types/qrcode": "^1.3.5",
|
"@types/qrcode": "^1.3.5",
|
||||||
"@types/react": "18.3.3",
|
"@types/react": "18.3.18",
|
||||||
"@types/react-beautiful-dnd": "^13.0.0",
|
"@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/react-transition-group": "^4.4.0",
|
||||||
"@types/sanitize-html": "2.13.0",
|
"@types/sanitize-html": "2.13.0",
|
||||||
"@types/sdp-transform": "^2.4.6",
|
|
||||||
"@types/seedrandom": "3.0.8",
|
|
||||||
"@types/semver": "^7.5.8",
|
"@types/semver": "^7.5.8",
|
||||||
"@types/tar-js": "^0.3.5",
|
"@types/tar-js": "^0.3.5",
|
||||||
"@types/ua-parser-js": "^0.7.36",
|
"@types/ua-parser-js": "^0.7.36",
|
||||||
"@types/uuid": "^10.0.0",
|
"@types/uuid": "^10.0.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
"@typescript-eslint/eslint-plugin": "^8.19.0",
|
||||||
"@typescript-eslint/parser": "^8.0.0",
|
"@typescript-eslint/parser": "^8.19.0",
|
||||||
"axe-core": "4.10.2",
|
|
||||||
"babel-jest": "^29.0.0",
|
"babel-jest": "^29.0.0",
|
||||||
"babel-loader": "^9.0.0",
|
"babel-loader": "^9.0.0",
|
||||||
"babel-plugin-jsx-remove-data-test-id": "^3.0.0",
|
"babel-plugin-jsx-remove-data-test-id": "^3.0.0",
|
||||||
"blob-polyfill": "^9.0.0",
|
"blob-polyfill": "^9.0.0",
|
||||||
"buffer": "^6.0.3",
|
|
||||||
"chokidar": "^4.0.0",
|
"chokidar": "^4.0.0",
|
||||||
"concurrently": "^9.0.0",
|
"concurrently": "^9.0.0",
|
||||||
"copy-webpack-plugin": "^12.0.0",
|
"copy-webpack-plugin": "^12.0.0",
|
||||||
@@ -234,13 +230,14 @@
|
|||||||
"dotenv": "^16.0.2",
|
"dotenv": "^16.0.2",
|
||||||
"eslint": "8.57.1",
|
"eslint": "8.57.1",
|
||||||
"eslint-config-google": "^0.14.0",
|
"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-deprecate": "0.8.5",
|
||||||
"eslint-plugin-import": "^2.25.4",
|
"eslint-plugin-import": "^2.25.4",
|
||||||
"eslint-plugin-jest": "^28.0.0",
|
"eslint-plugin-jest": "^28.0.0",
|
||||||
"eslint-plugin-jsx-a11y": "^6.5.1",
|
"eslint-plugin-jsx-a11y": "^6.5.1",
|
||||||
"eslint-plugin-matrix-org": "^2.0.2",
|
"eslint-plugin-matrix-org": "^2.0.2",
|
||||||
"eslint-plugin-react": "^7.28.0",
|
"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-react-hooks": "^5.0.0",
|
||||||
"eslint-plugin-unicorn": "^56.0.0",
|
"eslint-plugin-unicorn": "^56.0.0",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
@@ -248,7 +245,6 @@
|
|||||||
"fetch-mock": "9.11.0",
|
"fetch-mock": "9.11.0",
|
||||||
"fetch-mock-jest": "^1.5.1",
|
"fetch-mock-jest": "^1.5.1",
|
||||||
"file-loader": "^6.0.0",
|
"file-loader": "^6.0.0",
|
||||||
"fs-extra": "^11.0.0",
|
|
||||||
"glob": "^11.0.0",
|
"glob": "^11.0.0",
|
||||||
"html-webpack-plugin": "^5.5.3",
|
"html-webpack-plugin": "^5.5.3",
|
||||||
"husky": "^9.0.0",
|
"husky": "^9.0.0",
|
||||||
@@ -258,44 +254,48 @@
|
|||||||
"jest-mock": "^29.6.2",
|
"jest-mock": "^29.6.2",
|
||||||
"jest-raw-loader": "^1.0.1",
|
"jest-raw-loader": "^1.0.1",
|
||||||
"jsqr": "^1.4.0",
|
"jsqr": "^1.4.0",
|
||||||
|
"knip": "^5.36.2",
|
||||||
"lint-staged": "^15.0.2",
|
"lint-staged": "^15.0.2",
|
||||||
"mailhog": "^4.16.0",
|
"mailhog": "^4.16.0",
|
||||||
"matrix-mock-request": "^2.5.0",
|
|
||||||
"matrix-web-i18n": "^3.2.1",
|
"matrix-web-i18n": "^3.2.1",
|
||||||
"mini-css-extract-plugin": "2.9.0",
|
"mini-css-extract-plugin": "2.9.2",
|
||||||
"minimist": "^1.2.6",
|
"minimist": "^1.2.6",
|
||||||
"mkdirp": "^3.0.0",
|
|
||||||
"mocha-junit-reporter": "^2.2.0",
|
|
||||||
"modernizr": "^3.12.0",
|
"modernizr": "^3.12.0",
|
||||||
|
"node-fetch": "^2.6.7",
|
||||||
"playwright-core": "^1.45.1",
|
"playwright-core": "^1.45.1",
|
||||||
"postcss": "8.4.38",
|
"postcss": "8.4.46",
|
||||||
"postcss-easings": "^4.0.0",
|
"postcss-easings": "^4.0.0",
|
||||||
"postcss-hexrgba": "2.1.0",
|
"postcss-hexrgba": "2.1.0",
|
||||||
"postcss-import": "16.1.0",
|
"postcss-import": "16.1.0",
|
||||||
"postcss-loader": "8.1.1",
|
"postcss-loader": "8.1.1",
|
||||||
"postcss-mixins": "^11.0.0",
|
"postcss-mixins": "^11.0.0",
|
||||||
"postcss-nested": "^6.0.0",
|
"postcss-nested": "^7.0.0",
|
||||||
"postcss-preset-env": "^10.0.0",
|
"postcss-preset-env": "^10.0.0",
|
||||||
"postcss-scss": "^4.0.4",
|
"postcss-scss": "^4.0.4",
|
||||||
"postcss-simple-vars": "^7.0.1",
|
"postcss-simple-vars": "^7.0.1",
|
||||||
"prettier": "3.3.3",
|
"prettier": "3.4.2",
|
||||||
"process": "^0.11.10",
|
"process": "^0.11.10",
|
||||||
"raw-loader": "^4.0.2",
|
"raw-loader": "^4.0.2",
|
||||||
"rimraf": "^6.0.0",
|
"rimraf": "^6.0.0",
|
||||||
"semver": "^7.5.2",
|
"semver": "^7.5.2",
|
||||||
|
"source-map-loader": "^5.0.0",
|
||||||
|
"strip-ansi": "^7.1.0",
|
||||||
"stylelint": "^16.1.0",
|
"stylelint": "^16.1.0",
|
||||||
"stylelint-config-standard": "^36.0.0",
|
"stylelint-config-standard": "^36.0.0",
|
||||||
"stylelint-scss": "^6.0.0",
|
"stylelint-scss": "^6.0.0",
|
||||||
|
"stylelint-value-no-unknown-custom-properties": "^6.0.1",
|
||||||
"terser-webpack-plugin": "^5.3.9",
|
"terser-webpack-plugin": "^5.3.9",
|
||||||
|
"testcontainers": "^10.16.0",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"ts-prune": "^0.10.3",
|
"typescript": "5.7.3",
|
||||||
"typescript": "5.6.3",
|
|
||||||
"util": "^0.12.5",
|
"util": "^0.12.5",
|
||||||
"web-streams-polyfill": "^4.0.0",
|
"web-streams-polyfill": "^4.0.0",
|
||||||
"webpack": "^5.89.0",
|
"webpack": "^5.89.0",
|
||||||
"webpack-bundle-analyzer": "^4.8.0",
|
"webpack-bundle-analyzer": "^4.8.0",
|
||||||
"webpack-cli": "^5.0.0",
|
"webpack-cli": "^6.0.0",
|
||||||
"webpack-dev-server": "^5.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"
|
"yaml": "^2.3.3"
|
||||||
},
|
},
|
||||||
"@casualbot/jest-sonar-reporter": {
|
"@casualbot/jest-sonar-reporter": {
|
||||||
|
|||||||
@@ -2,24 +2,80 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
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.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { defineConfig } from "@playwright/test";
|
import { defineConfig, devices } from "@playwright/test";
|
||||||
|
|
||||||
|
import { Options } from "./playwright/services";
|
||||||
|
|
||||||
const baseURL = process.env["BASE_URL"] ?? "http://localhost:8080";
|
const baseURL = process.env["BASE_URL"] ?? "http://localhost:8080";
|
||||||
|
|
||||||
export default defineConfig({
|
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: {
|
use: {
|
||||||
viewport: { width: 1280, height: 720 },
|
viewport: { width: 1280, height: 720 },
|
||||||
ignoreHTTPSErrors: true,
|
ignoreHTTPSErrors: true,
|
||||||
video: "retain-on-failure",
|
video: "retain-on-failure",
|
||||||
baseURL,
|
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",
|
trace: "on-first-retry",
|
||||||
},
|
},
|
||||||
webServer: {
|
webServer: {
|
||||||
|
|||||||
2
playwright/@types/playwright-core.d.ts
vendored
2
playwright/@types/playwright-core.d.ts
vendored
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2024 The Matrix.org Foundation C.I.C.
|
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.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM mcr.microsoft.com/playwright:v1.48.2-jammy
|
FROM mcr.microsoft.com/playwright:v1.49.1-noble
|
||||||
|
|
||||||
WORKDIR /work
|
WORKDIR /work
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2024 The Matrix.org Foundation C.I.C.
|
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.
|
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();
|
await expect(page.getByText("Bob joined the room")).toBeVisible();
|
||||||
|
|
||||||
// Close the room
|
// Close the room
|
||||||
page.goto("/#/home");
|
await page.goto("/#/home");
|
||||||
|
|
||||||
// Pressing Control+F6 will first focus the space button
|
// Pressing Control+F6 will first focus the space button
|
||||||
await page.keyboard.press("ControlOrMeta+F6");
|
await page.keyboard.press("ControlOrMeta+F6");
|
||||||
|
|||||||
@@ -2,13 +2,13 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2024 The Matrix.org Foundation C.I.C.
|
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.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { test, expect } from "../../element-web-test";
|
import { test, expect } from "../../element-web-test";
|
||||||
|
|
||||||
test(`shows error page if browser lacks Intl support`, async ({ page }) => {
|
test(`shows error page if browser lacks Intl support`, { tag: "@screenshot" }, async ({ page }) => {
|
||||||
await page.addInitScript({ content: `delete window.Intl;` });
|
await page.addInitScript({ content: `delete window.Intl;` });
|
||||||
await page.goto("/");
|
await page.goto("/");
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ test(`shows error page if browser lacks Intl support`, async ({ page }) => {
|
|||||||
await expect(page).toMatchScreenshot("unsupported-browser.png");
|
await expect(page).toMatchScreenshot("unsupported-browser.png");
|
||||||
});
|
});
|
||||||
|
|
||||||
test(`shows error page if browser lacks WebAssembly support`, async ({ page }) => {
|
test(`shows error page if browser lacks WebAssembly support`, { tag: "@screenshot" }, async ({ page }) => {
|
||||||
await page.addInitScript({ content: `delete window.WebAssembly;` });
|
await page.addInitScript({ content: `delete window.WebAssembly;` });
|
||||||
await page.goto("/");
|
await page.goto("/");
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2024 The Matrix.org Foundation C.I.C.
|
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.
|
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";
|
import { expect, test } from "../../element-web-test";
|
||||||
|
|
||||||
test.use({
|
test.use({
|
||||||
startHomeserverOpts: "guest-enabled",
|
synapseConfig: {
|
||||||
config: async ({ homeserver }, use) => {
|
allow_guest_access: true,
|
||||||
await use({
|
|
||||||
default_server_config: {
|
|
||||||
"m.homeserver": { base_url: homeserver.config.baseUrl },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2024 The Matrix.org Foundation C.I.C.
|
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.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ Copyright 2024 New Vector Ltd.
|
|||||||
Copyright 2023 Suguru Hirahara
|
Copyright 2023 Suguru Hirahara
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
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.
|
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 { Layout } from "../../../src/settings/enums/Layout";
|
||||||
import { ElementAppPage } from "../../pages/ElementAppPage";
|
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({
|
test.use({
|
||||||
displayName: "Hanako",
|
displayName: "Hanako",
|
||||||
});
|
});
|
||||||
@@ -134,18 +142,22 @@ test.describe("Audio player", () => {
|
|||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should be correctly rendered - light theme", async ({ page, app }) => {
|
test("should be correctly rendered - light theme", { tag: "@screenshot" }, async ({ page, app }) => {
|
||||||
await uploadFile(page, "playwright/sample-files/1sec-long-name-audio-file.ogg");
|
await uploadFile(page, "playwright/sample-files/1sec-long-name-audio-file.ogg");
|
||||||
await takeSnapshots(page, app, "Selected EventTile of audio player (light theme)");
|
await takeSnapshots(page, app, "Selected EventTile of audio player (light theme)");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should be correctly rendered - light theme with monospace font", async ({ page, app }) => {
|
test(
|
||||||
|
"should be correctly rendered - light theme with monospace font",
|
||||||
|
{ tag: "@screenshot" },
|
||||||
|
async ({ page, app }) => {
|
||||||
await uploadFile(page, "playwright/sample-files/1sec-long-name-audio-file.ogg");
|
await uploadFile(page, "playwright/sample-files/1sec-long-name-audio-file.ogg");
|
||||||
|
|
||||||
await takeSnapshots(page, app, "Selected EventTile of audio player (light theme, monospace font)", true); // Enable monospace
|
await takeSnapshots(page, app, "Selected EventTile of audio player (light theme, monospace font)", true); // Enable monospace
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
test("should be correctly rendered - high contrast theme", async ({ page, app }) => {
|
test("should be correctly rendered - high contrast theme", { tag: "@screenshot" }, async ({ page, app }) => {
|
||||||
// Disable system theme in case ThemeWatcher enables the theme automatically,
|
// Disable system theme in case ThemeWatcher enables the theme automatically,
|
||||||
// so that the high contrast theme can be enabled
|
// so that the high contrast theme can be enabled
|
||||||
await app.settings.setValue("use_system_theme", null, SettingLevel.DEVICE, false);
|
await app.settings.setValue("use_system_theme", null, SettingLevel.DEVICE, false);
|
||||||
@@ -161,7 +173,7 @@ test.describe("Audio player", () => {
|
|||||||
await takeSnapshots(page, app, "Selected EventTile of audio player (high contrast)");
|
await takeSnapshots(page, app, "Selected EventTile of audio player (high contrast)");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should be correctly rendered - dark theme", async ({ page, app }) => {
|
test("should be correctly rendered - dark theme", { tag: "@screenshot" }, async ({ page, app }) => {
|
||||||
// Enable dark theme
|
// Enable dark theme
|
||||||
await app.settings.setValue("theme", null, SettingLevel.ACCOUNT, "dark");
|
await app.settings.setValue("theme", null, SettingLevel.ACCOUNT, "dark");
|
||||||
|
|
||||||
@@ -207,7 +219,10 @@ test.describe("Audio player", () => {
|
|||||||
expect(download.suggestedFilename()).toBe("1sec.ogg");
|
expect(download.suggestedFilename()).toBe("1sec.ogg");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should support replying to audio file with another audio file", async ({ page, app }) => {
|
test(
|
||||||
|
"should support replying to audio file with another audio file",
|
||||||
|
{ tag: "@screenshot" },
|
||||||
|
async ({ page, app }) => {
|
||||||
await uploadFile(page, "playwright/sample-files/1sec.ogg");
|
await uploadFile(page, "playwright/sample-files/1sec.ogg");
|
||||||
|
|
||||||
// Assert the audio player is rendered
|
// Assert the audio player is rendered
|
||||||
@@ -215,8 +230,7 @@ test.describe("Audio player", () => {
|
|||||||
|
|
||||||
// Find and click "Reply" button on MessageActionBar
|
// Find and click "Reply" button on MessageActionBar
|
||||||
const tile = page.locator(".mx_EventTile_last");
|
const tile = page.locator(".mx_EventTile_last");
|
||||||
await tile.hover();
|
await clickButtonReply(tile);
|
||||||
await tile.getByRole("button", { name: "Reply", exact: true }).click();
|
|
||||||
|
|
||||||
// Reply to the player with another audio file
|
// Reply to the player with another audio file
|
||||||
await uploadFile(page, "playwright/sample-files/1sec.ogg");
|
await uploadFile(page, "playwright/sample-files/1sec.ogg");
|
||||||
@@ -230,9 +244,13 @@ test.describe("Audio player", () => {
|
|||||||
await expect(button.locator(".mx_MFileBody_info_filename")).toBeVisible();
|
await expect(button.locator(".mx_MFileBody_info_filename")).toBeVisible();
|
||||||
|
|
||||||
await takeSnapshots(page, app, "Selected EventTile of audio player with a reply");
|
await takeSnapshots(page, app, "Selected EventTile of audio player with a reply");
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
test("should support creating a reply chain with multiple audio files", async ({ page, app, user }) => {
|
test(
|
||||||
|
"should support creating a reply chain with multiple audio files",
|
||||||
|
{ tag: "@screenshot" },
|
||||||
|
async ({ page, app, user }) => {
|
||||||
// Note: "mx_ReplyChain" element is used not only for replies which
|
// Note: "mx_ReplyChain" element is used not only for replies which
|
||||||
// create a reply chain, but also for a single reply without a replied
|
// create a reply chain, but also for a single reply without a replied
|
||||||
// message. This test checks whether a reply chain which consists of
|
// message. This test checks whether a reply chain which consists of
|
||||||
@@ -240,19 +258,12 @@ test.describe("Audio player", () => {
|
|||||||
|
|
||||||
const tile = page.locator(".mx_EventTile_last");
|
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");
|
await uploadFile(page, "playwright/sample-files/upload-first.ogg");
|
||||||
|
|
||||||
// Assert that the audio player is rendered
|
// Assert that the audio player is rendered
|
||||||
await expect(page.locator(".mx_EventTile_last .mx_AudioPlayer_container")).toBeVisible();
|
await expect(page.locator(".mx_EventTile_last .mx_AudioPlayer_container")).toBeVisible();
|
||||||
|
|
||||||
await clickButtonReply();
|
await clickButtonReply(tile);
|
||||||
|
|
||||||
// Reply to the player with another audio file
|
// Reply to the player with another audio file
|
||||||
await uploadFile(page, "playwright/sample-files/upload-second.ogg");
|
await uploadFile(page, "playwright/sample-files/upload-second.ogg");
|
||||||
@@ -260,7 +271,7 @@ test.describe("Audio player", () => {
|
|||||||
// Assert that the audio player is rendered
|
// Assert that the audio player is rendered
|
||||||
await expect(page.locator(".mx_EventTile_last .mx_AudioPlayer_container")).toBeVisible();
|
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
|
// Reply to the player with yet another audio file to create a reply chain
|
||||||
await uploadFile(page, "playwright/sample-files/upload-third.ogg");
|
await uploadFile(page, "playwright/sample-files/upload-third.ogg");
|
||||||
@@ -293,7 +304,8 @@ test.describe("Audio player", () => {
|
|||||||
|
|
||||||
// Take snapshots
|
// Take snapshots
|
||||||
await takeSnapshots(page, app, "Selected EventTile of audio player with a reply chain");
|
await takeSnapshots(page, app, "Selected EventTile of audio player with a reply chain");
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
test("should be rendered, play, and support replying on a thread", async ({ page, app }) => {
|
test("should be rendered, play, and support replying on a thread", async ({ page, app }) => {
|
||||||
await uploadFile(page, "playwright/sample-files/1sec-long-name-audio-file.ogg");
|
await uploadFile(page, "playwright/sample-files/1sec-long-name-audio-file.ogg");
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2024 The Matrix.org Foundation C.I.C.
|
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.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -89,10 +89,13 @@ test.describe("HTML Export", () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should export html successfully and match screenshot", async ({ page, app, room }) => {
|
test(
|
||||||
|
"should export html successfully and match screenshot",
|
||||||
|
{ tag: "@screenshot" },
|
||||||
|
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
|
// 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.
|
// 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
|
// Send a bunch of messages to populate the room
|
||||||
for (let i = 1; i < 10; i++) {
|
for (let i = 1; i < 10; i++) {
|
||||||
@@ -127,5 +130,6 @@ test.describe("HTML Export", () => {
|
|||||||
page.locator(".mx_MessageTimestamp"),
|
page.locator(".mx_MessageTimestamp"),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
});
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
|
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.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
|
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.
|
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
|
// Enter some more text, then send the message
|
||||||
await page.getByRole("textbox").pressSequentially("this is the spoiler text ");
|
await page.getByRole("textbox").pressSequentially("this is the spoiler text ");
|
||||||
await page.getByRole("button", { name: "Send message" }).click();
|
await page.getByRole("button", { name: "Send message" }).click();
|
||||||
// Check that a spoiler item has appeared in the timeline and locator the spoiler command text
|
// Check that a spoiler item has appeared in the timeline and contains the spoiler text
|
||||||
await expect(page.locator("button.mx_EventTile_spoiler")).toBeVisible();
|
await expect(page.locator("button.mx_EventTile_spoiler")).toHaveText("this is the spoiler text");
|
||||||
await expect(page.getByText("this is the spoiler text")).toBeVisible();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -166,7 +165,7 @@ test.describe("Composer", () => {
|
|||||||
// Type another
|
// Type another
|
||||||
await page.locator("div[contenteditable=true]").pressSequentially("my message 1");
|
await page.locator("div[contenteditable=true]").pressSequentially("my message 1");
|
||||||
// Send message
|
// Send message
|
||||||
page.locator("div[contenteditable=true]").press("Enter");
|
await page.locator("div[contenteditable=true]").press("Enter");
|
||||||
// It was sent
|
// It was sent
|
||||||
await expect(page.locator(".mx_EventTile_last .mx_EventTile_body").getByText("my message 1")).toBeVisible();
|
await expect(page.locator(".mx_EventTile_last .mx_EventTile_body").getByText("my message 1")).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
|
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.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ test.describe("Create Room", () => {
|
|||||||
// Submit
|
// Submit
|
||||||
await dialog.getByRole("button", { name: "Create room" }).click();
|
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");
|
const header = page.locator(".mx_RoomHeader");
|
||||||
await expect(header).toContainText(name);
|
await expect(header).toContainText(name);
|
||||||
});
|
});
|
||||||
|
|||||||
95
playwright/e2e/crypto/backups-mas.spec.ts
Normal file
95
playwright/e2e/crypto/backups-mas.spec.ts
Normal 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, mailhogClient, app }, testInfo) => {
|
||||||
|
await page.goto("/#/login");
|
||||||
|
await page.getByRole("button", { name: "Continue" }).click();
|
||||||
|
await registerAccountMas(page, mailhogClient, `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, mailhogClient, app }, testInfo) => {
|
||||||
|
await page.goto("/#/login");
|
||||||
|
await page.getByRole("button", { name: "Continue" }).click();
|
||||||
|
await registerAccountMas(page, mailhogClient, `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,
|
||||||
|
mailhogClient,
|
||||||
|
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, mailhogClient, 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();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -2,13 +2,14 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
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.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { type Page } from "@playwright/test";
|
import { type Page } from "@playwright/test";
|
||||||
|
|
||||||
import { test, expect } from "../../element-web-test";
|
import { test, expect } from "../../element-web-test";
|
||||||
|
import { isDendrite } from "../../plugins/homeserver/dendrite";
|
||||||
|
|
||||||
async function expectBackupVersionToBe(page: Page, version: string) {
|
async function expectBackupVersionToBe(page: Page, version: string) {
|
||||||
await expect(page.locator(".mx_SecureBackupPanel_statusList tr:nth-child(5) td")).toHaveText(
|
await expect(page.locator(".mx_SecureBackupPanel_statusList tr:nth-child(5) td")).toHaveText(
|
||||||
@@ -19,11 +20,15 @@ async function expectBackupVersionToBe(page: Page, version: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test.describe("Backups", () => {
|
test.describe("Backups", () => {
|
||||||
|
test.skip(isDendrite, "Dendrite lacks support for MSC3967 so requires additional auth here");
|
||||||
test.use({
|
test.use({
|
||||||
displayName: "Hanako",
|
displayName: "Hanako",
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Create, delete and recreate a keys backup", async ({ page, user, app }, workerInfo) => {
|
test(
|
||||||
|
"Create, delete and recreate a keys backup",
|
||||||
|
{ tag: "@no-webkit" },
|
||||||
|
async ({ page, user, app }, workerInfo) => {
|
||||||
// Create a backup
|
// Create a backup
|
||||||
const securityTab = await app.settings.openUserSettings("Security & Privacy");
|
const securityTab = await app.settings.openUserSettings("Security & Privacy");
|
||||||
|
|
||||||
@@ -108,5 +113,6 @@ test.describe("Backups", () => {
|
|||||||
// go back to the settings to check that no backup was created (the setup button should still be there)
|
// 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 app.settings.openUserSettings("Security & Privacy");
|
||||||
await expect(securityTab.getByRole("button", { name: "Set up", exact: true })).toBeVisible();
|
await expect(securityTab.getByRole("button", { name: "Set up", exact: true })).toBeVisible();
|
||||||
});
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,14 +2,16 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
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.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { test, expect } from "../../element-web-test";
|
import { test, expect } from "../../element-web-test";
|
||||||
import { logIntoElement } from "./utils";
|
import { logIntoElement } from "./utils";
|
||||||
|
import { isDendrite } from "../../plugins/homeserver/dendrite";
|
||||||
|
|
||||||
test.describe("Complete security", () => {
|
test.describe("Complete security", () => {
|
||||||
|
test.skip(isDendrite, "Dendrite lacks support for MSC3967 so requires additional auth here");
|
||||||
test.use({
|
test.use({
|
||||||
displayName: "Jeff",
|
displayName: "Jeff",
|
||||||
});
|
});
|
||||||
@@ -19,9 +21,9 @@ test.describe("Complete security", () => {
|
|||||||
homeserver,
|
homeserver,
|
||||||
credentials,
|
credentials,
|
||||||
}) => {
|
}) => {
|
||||||
await logIntoElement(page, homeserver, credentials);
|
await logIntoElement(page, credentials);
|
||||||
await expect(page.getByText("Welcome Jeff", { exact: true })).toBeVisible();
|
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`.
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
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.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -11,6 +11,7 @@ import { expect, test } from "../../element-web-test";
|
|||||||
import { autoJoin, copyAndContinue, createSharedRoomWithUser, enableKeyBackup, verify } from "./utils";
|
import { autoJoin, copyAndContinue, createSharedRoomWithUser, enableKeyBackup, verify } from "./utils";
|
||||||
import { Bot } from "../../pages/bot";
|
import { Bot } from "../../pages/bot";
|
||||||
import { ElementAppPage } from "../../pages/ElementAppPage";
|
import { ElementAppPage } from "../../pages/ElementAppPage";
|
||||||
|
import { isDendrite } from "../../plugins/homeserver/dendrite";
|
||||||
|
|
||||||
const checkDMRoom = async (page: Page) => {
|
const checkDMRoom = async (page: Page) => {
|
||||||
const body = page.locator(".mx_RoomView_body");
|
const body = page.locator(".mx_RoomView_body");
|
||||||
@@ -67,6 +68,7 @@ const bobJoin = async (page: Page, bob: Bot) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
test.describe("Cryptography", function () {
|
test.describe("Cryptography", function () {
|
||||||
|
test.skip(isDendrite, "Dendrite lacks support for MSC3967 so requires additional auth here");
|
||||||
test.use({
|
test.use({
|
||||||
displayName: "Alice",
|
displayName: "Alice",
|
||||||
botCreateOpts: {
|
botCreateOpts: {
|
||||||
@@ -81,7 +83,7 @@ test.describe("Cryptography", function () {
|
|||||||
* Verify that the `m.cross_signing.${keyType}` key is available on the account data on the server
|
* Verify that the `m.cross_signing.${keyType}` key is available on the account data on the server
|
||||||
* @param keyType
|
* @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(
|
const accountData: { encrypted: Record<string, Record<string, string>> } = await app.client.evaluate(
|
||||||
(cli, keyType) => cli.getAccountDataFromServer(`m.cross_signing.${keyType}`),
|
(cli, keyType) => cli.getAccountDataFromServer(`m.cross_signing.${keyType}`),
|
||||||
keyType,
|
keyType,
|
||||||
@@ -204,12 +206,10 @@ test.describe("Cryptography", function () {
|
|||||||
await expect(page.locator(".mx_Dialog")).toHaveCount(1);
|
await expect(page.locator(".mx_Dialog")).toHaveCount(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("creating a DM should work, being e2e-encrypted / user verification", async ({
|
test(
|
||||||
page,
|
"creating a DM should work, being e2e-encrypted / user verification",
|
||||||
app,
|
{ tag: "@screenshot" },
|
||||||
bot: bob,
|
async ({ page, app, bot: bob, user: aliceCredentials }) => {
|
||||||
user: aliceCredentials,
|
|
||||||
}) => {
|
|
||||||
await app.client.bootstrapCrossSigning(aliceCredentials);
|
await app.client.bootstrapCrossSigning(aliceCredentials);
|
||||||
await startDMWithBob(page, bob);
|
await startDMWithBob(page, bob);
|
||||||
// send first message
|
// send first message
|
||||||
@@ -227,7 +227,8 @@ test.describe("Cryptography", function () {
|
|||||||
|
|
||||||
// Take a snapshot of RoomSummaryCard with a verified E2EE icon
|
// Take a snapshot of RoomSummaryCard with a verified E2EE icon
|
||||||
await expect(page.locator(".mx_RightPanel")).toMatchScreenshot("RoomSummaryCard-with-verified-e2ee.png");
|
await expect(page.locator(".mx_RightPanel")).toMatchScreenshot("RoomSummaryCard-with-verified-e2ee.png");
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
test("should allow verification when there is no existing DM", async ({
|
test("should allow verification when there is no existing DM", async ({
|
||||||
page,
|
page,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
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.
|
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.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 ({
|
test("should handle device-relative historical messages", async ({
|
||||||
homeserver,
|
homeserver,
|
||||||
page,
|
page,
|
||||||
@@ -45,7 +47,7 @@ test.describe("Cryptography", function () {
|
|||||||
await logOutOfElement(page, true);
|
await logOutOfElement(page, true);
|
||||||
|
|
||||||
// Log in again, and see how the message looks.
|
// Log in again, and see how the message looks.
|
||||||
await logIntoElement(page, homeserver, credentials);
|
await logIntoElement(page, credentials);
|
||||||
await app.viewRoomByName("Test room");
|
await app.viewRoomByName("Test room");
|
||||||
const lastTile = page.locator(".mx_EventTile").last();
|
const lastTile = page.locator(".mx_EventTile").last();
|
||||||
await expect(lastTile).toContainText("Historical messages are not available on this device");
|
await expect(lastTile).toContainText("Historical messages are not available on this device");
|
||||||
@@ -62,11 +64,14 @@ test.describe("Cryptography", function () {
|
|||||||
|
|
||||||
// Finally, log out again, and back in, skipping verification for now, and see what we see.
|
// Finally, log out again, and back in, skipping verification for now, and see what we see.
|
||||||
await logOutOfElement(page);
|
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: "Skip verification for now" }).click();
|
||||||
await page.locator(".mx_AuthPage").getByRole("button", { name: "I'll verify later" }).click();
|
await page.locator(".mx_AuthPage").getByRole("button", { name: "I'll verify later" }).click();
|
||||||
await app.viewRoomByName("Test room");
|
await app.viewRoomByName("Test room");
|
||||||
|
|
||||||
|
// In this case, the call to cryptoApi.isEncryptionEnabledInRoom is taking a long time to resolve
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
|
||||||
// There should be two historical events in the timeline
|
// There should be two historical events in the timeline
|
||||||
const tiles = await page.locator(".mx_EventTile").all();
|
const tiles = await page.locator(".mx_EventTile").all();
|
||||||
expect(tiles.length).toBeGreaterThanOrEqual(2);
|
expect(tiles.length).toBeGreaterThanOrEqual(2);
|
||||||
|
|||||||
@@ -2,26 +2,34 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2024 The Matrix.org Foundation C.I.C.
|
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.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Locator, type Page } from "@playwright/test";
|
import { Locator, type Page } from "@playwright/test";
|
||||||
|
|
||||||
import { test as base, expect } from "../../element-web-test";
|
import { test, expect } from "../../element-web-test";
|
||||||
import { viewRoomSummaryByName } from "../right-panel/utils";
|
import { viewRoomSummaryByName } from "../right-panel/utils";
|
||||||
import { isDendrite } from "../../plugins/homeserver/dendrite";
|
import { isDendrite } from "../../plugins/homeserver/dendrite";
|
||||||
|
|
||||||
const test = base.extend({
|
const ROOM_NAME = "Test room";
|
||||||
// eslint-disable-next-line no-empty-pattern
|
const NAME = "Alice";
|
||||||
startHomeserverOpts: async ({}, use) => {
|
|
||||||
await use("dehydration");
|
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 = {
|
const wellKnown = {
|
||||||
"m.homeserver": {
|
...config.default_server_config,
|
||||||
base_url: homeserver.config.baseUrl,
|
|
||||||
},
|
|
||||||
"org.matrix.msc3814": true,
|
"org.matrix.msc3814": true,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -29,29 +37,14 @@ const test = base.extend({
|
|||||||
await route.fulfill({ json: wellKnown });
|
await route.fulfill({ json: wellKnown });
|
||||||
});
|
});
|
||||||
|
|
||||||
await use({
|
await use(config);
|
||||||
default_server_config: wellKnown,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
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.describe("Dehydration", () => {
|
||||||
test.skip(isDendrite, "does not yet support dehydration v2");
|
test.skip(isDendrite, "does not yet support dehydration v2");
|
||||||
|
|
||||||
test.use({
|
|
||||||
displayName: NAME,
|
|
||||||
});
|
|
||||||
|
|
||||||
test("Create dehydrated device", async ({ page, user, app }, workerInfo) => {
|
test("Create dehydrated device", async ({ page, user, app }, workerInfo) => {
|
||||||
test.skip(workerInfo.project.name === "Legacy Crypto", "This test only works with Rust crypto.");
|
|
||||||
|
|
||||||
// Create a backup (which will create SSSS, and dehydrated device)
|
// Create a backup (which will create SSSS, and dehydrated device)
|
||||||
|
|
||||||
const securityTab = await app.settings.openUserSettings("Security & Privacy");
|
const securityTab = await app.settings.openUserSettings("Security & Privacy");
|
||||||
@@ -95,7 +88,7 @@ test.describe("Dehydration", () => {
|
|||||||
await viewRoomSummaryByName(page, app, ROOM_NAME);
|
await viewRoomSummaryByName(page, app, ROOM_NAME);
|
||||||
|
|
||||||
await page.locator(".mx_RightPanel").getByRole("menuitem", { name: "People" }).click();
|
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 getMemberTileByName(page, NAME).click();
|
||||||
await page.locator(".mx_UserInfo_devices .mx_UserInfo_expand").click();
|
await page.locator(".mx_UserInfo_devices .mx_UserInfo_expand").click();
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
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.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -15,42 +15,23 @@ import {
|
|||||||
awaitVerifier,
|
awaitVerifier,
|
||||||
checkDeviceIsConnectedKeyBackup,
|
checkDeviceIsConnectedKeyBackup,
|
||||||
checkDeviceIsCrossSigned,
|
checkDeviceIsCrossSigned,
|
||||||
|
createBot,
|
||||||
doTwoWaySasVerification,
|
doTwoWaySasVerification,
|
||||||
logIntoElement,
|
logIntoElement,
|
||||||
waitForVerificationRequest,
|
waitForVerificationRequest,
|
||||||
} from "./utils";
|
} from "./utils";
|
||||||
import { Bot } from "../../pages/bot";
|
import { Bot } from "../../pages/bot";
|
||||||
|
|
||||||
test.describe("Device verification", () => {
|
test.describe("Device verification", { tag: "@no-webkit" }, () => {
|
||||||
let aliceBotClient: Bot;
|
let aliceBotClient: Bot;
|
||||||
|
|
||||||
/** The backup version that was set up by the bot client. */
|
/** The backup version that was set up by the bot client. */
|
||||||
let expectedBackupVersion: string;
|
let expectedBackupVersion: string;
|
||||||
|
|
||||||
test.beforeEach(async ({ page, homeserver, credentials }) => {
|
test.beforeEach(async ({ page, homeserver, credentials }) => {
|
||||||
// Visit the login page of the app, to load the matrix sdk
|
const res = await createBot(page, homeserver, credentials);
|
||||||
await page.goto("/#/login");
|
aliceBotClient = res.botClient;
|
||||||
|
expectedBackupVersion = res.expectedBackupVersion;
|
||||||
// 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);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Click the "Verify with another device" button, and have the bot client auto-accept it.
|
// 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 }) => {
|
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
|
// Launch the verification request between alice and the bot
|
||||||
const verificationRequest = await initiateAliceVerificationRequest(page);
|
const verificationRequest = await initiateAliceVerificationRequest(page);
|
||||||
@@ -93,7 +74,7 @@ test.describe("Device verification", () => {
|
|||||||
|
|
||||||
test("Verify device with QR code during login", async ({ page, app, credentials, homeserver }) => {
|
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"
|
// 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
|
// Launch the verification request between alice and the bot
|
||||||
const verificationRequest = await initiateAliceVerificationRequest(page);
|
const verificationRequest = await initiateAliceVerificationRequest(page);
|
||||||
@@ -102,7 +83,7 @@ test.describe("Device verification", () => {
|
|||||||
// feed the QR code into the verification request.
|
// feed the QR code into the verification request.
|
||||||
const qrData = await readQrCode(infoDialog);
|
const qrData = await readQrCode(infoDialog);
|
||||||
const verifier = await verificationRequest.evaluateHandle(
|
const verifier = await verificationRequest.evaluateHandle(
|
||||||
(request, qrData) => request.scanQRCode(new Uint8Array(qrData)),
|
(request, qrData) => request.scanQRCode(new Uint8ClampedArray(qrData)),
|
||||||
[...qrData],
|
[...qrData],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -137,7 +118,7 @@ test.describe("Device verification", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("Verify device with Security Phrase during login", async ({ page, app, credentials, homeserver }) => {
|
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
|
// Select the security phrase
|
||||||
await page.locator(".mx_AuthPage").getByRole("button", { name: "Verify with Security Key or Phrase" }).click();
|
await page.locator(".mx_AuthPage").getByRole("button", { name: "Verify with Security Key or Phrase" }).click();
|
||||||
@@ -158,7 +139,7 @@ test.describe("Device verification", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("Verify device with Security Key during login", async ({ page, app, credentials, homeserver }) => {
|
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
|
// Select the security phrase
|
||||||
await page.locator(".mx_AuthPage").getByRole("button", { name: "Verify with Security Key or Phrase" }).click();
|
await page.locator(".mx_AuthPage").getByRole("button", { name: "Verify with Security Key or Phrase" }).click();
|
||||||
@@ -181,7 +162,7 @@ test.describe("Device verification", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("Handle incoming verification request with SAS", async ({ page, credentials, homeserver, toasts }) => {
|
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" */
|
/* Dismiss "Verify this device" */
|
||||||
const authPage = page.locator(".mx_AuthPage");
|
const authPage = page.locator(".mx_AuthPage");
|
||||||
@@ -212,16 +193,17 @@ test.describe("Device verification", () => {
|
|||||||
/* on the bot side, wait for the verifier to exist ... */
|
/* on the bot side, wait for the verifier to exist ... */
|
||||||
const verifier = await awaitVerifier(botVerificationRequest);
|
const verifier = await awaitVerifier(botVerificationRequest);
|
||||||
// ... confirm ...
|
// ... confirm ...
|
||||||
botVerificationRequest.evaluate((verificationRequest) => verificationRequest.verifier.verify());
|
void botVerificationRequest.evaluate((verificationRequest) => verificationRequest.verifier.verify());
|
||||||
// ... and then check the emoji match
|
// ... and then check the emoji match
|
||||||
await doTwoWaySasVerification(page, verifier);
|
await doTwoWaySasVerification(page, verifier);
|
||||||
|
|
||||||
/* And we're all done! */
|
/* And we're all done! */
|
||||||
const infoDialog = page.locator(".mx_InfoDialog");
|
const infoDialog = page.locator(".mx_InfoDialog");
|
||||||
await infoDialog.getByRole("button", { name: "They match" }).click();
|
await infoDialog.getByRole("button", { name: "They match" }).click();
|
||||||
await expect(
|
// We don't assert the full string as the device name is unset on Synapse but set to the user ID on Dendrite
|
||||||
infoDialog.getByText(`You've successfully verified (${aliceBotClient.credentials.deviceId})!`),
|
await expect(infoDialog.getByText(`You've successfully verified`)).toContainText(
|
||||||
).toBeVisible();
|
`(${aliceBotClient.credentials.deviceId})`,
|
||||||
|
);
|
||||||
await infoDialog.getByRole("button", { name: "Got it" }).click();
|
await infoDialog.getByRole("button", { name: "Got it" }).click();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,10 +2,12 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
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.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { Locator } from "@playwright/test";
|
||||||
|
|
||||||
import { expect, test } from "../../element-web-test";
|
import { expect, test } from "../../element-web-test";
|
||||||
import {
|
import {
|
||||||
autoJoin,
|
autoJoin,
|
||||||
@@ -16,6 +18,8 @@ import {
|
|||||||
logOutOfElement,
|
logOutOfElement,
|
||||||
verify,
|
verify,
|
||||||
} from "./utils";
|
} from "./utils";
|
||||||
|
import { bootstrapCrossSigningForClient } from "../../pages/client.ts";
|
||||||
|
import { ElementAppPage } from "../../pages/ElementAppPage.ts";
|
||||||
|
|
||||||
test.describe("Cryptography", function () {
|
test.describe("Cryptography", function () {
|
||||||
test.use({
|
test.use({
|
||||||
@@ -49,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.
|
// 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 bob.awaitRoomMembership(testRoomId);
|
||||||
|
|
||||||
|
await app.client.network.setupRoute();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should show the correct shield on e2e events", async ({
|
test("should show the correct shield on e2e events", async ({
|
||||||
@@ -60,6 +66,9 @@ test.describe("Cryptography", function () {
|
|||||||
// Bob has a second, not cross-signed, device
|
// Bob has a second, not cross-signed, device
|
||||||
const bobSecondDevice = await createSecondBotDevice(page, homeserver, bob);
|
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", {
|
await bob.sendEvent(testRoomId, null, "m.room.encrypted", {
|
||||||
algorithm: "m.megolm.v1.aes-sha2",
|
algorithm: "m.megolm.v1.aes-sha2",
|
||||||
ciphertext: "the bird is in the hand",
|
ciphertext: "the bird is in the hand",
|
||||||
@@ -129,8 +138,7 @@ test.describe("Cryptography", function () {
|
|||||||
"Encrypted by a device not verified by its owner.",
|
"Encrypted by a device not verified by its owner.",
|
||||||
);
|
);
|
||||||
|
|
||||||
/* In legacy crypto: should show a grey padlock for a message from a deleted device.
|
/* Should show a red padlock for a message from an unverified device.
|
||||||
* In rust crypto: should show a red padlock for a message from an unverified device.
|
|
||||||
* Rust crypto remembers the verification state of the sending device, so it will know that the device was
|
* Rust crypto remembers the verification state of the sending device, so it will know that the device was
|
||||||
* unverified, even if it gets deleted. */
|
* unverified, even if it gets deleted. */
|
||||||
// bob deletes his second device
|
// bob deletes his second device
|
||||||
@@ -164,9 +172,7 @@ test.describe("Cryptography", function () {
|
|||||||
await expect(lastE2eIcon).toHaveClass(/mx_EventTile_e2eIcon_warning/);
|
await expect(lastE2eIcon).toHaveClass(/mx_EventTile_e2eIcon_warning/);
|
||||||
await lastE2eIcon.focus();
|
await lastE2eIcon.focus();
|
||||||
await expect(await app.getTooltipForElement(lastE2eIcon)).toContainText(
|
await expect(await app.getTooltipForElement(lastE2eIcon)).toContainText(
|
||||||
workerInfo.project.name === "Legacy Crypto"
|
"Encrypted by a device not verified by its owner.",
|
||||||
? "Encrypted by an unknown or deleted device."
|
|
||||||
: "Encrypted by a device not verified by its owner.",
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -204,7 +210,7 @@ test.describe("Cryptography", function () {
|
|||||||
window.localStorage.clear();
|
window.localStorage.clear();
|
||||||
});
|
});
|
||||||
await page.reload();
|
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 */
|
/* go back to the test room and find Bob's message again */
|
||||||
await app.viewRoomById(testRoomId);
|
await app.viewRoomById(testRoomId);
|
||||||
@@ -276,6 +282,15 @@ test.describe("Cryptography", function () {
|
|||||||
bot: bob,
|
bot: bob,
|
||||||
homeserver,
|
homeserver,
|
||||||
}) => {
|
}) => {
|
||||||
|
// Workaround for https://github.com/element-hq/element-web/issues/28640:
|
||||||
|
// make sure that Alice has seen Bob's identity before she goes offline. We do this by opening
|
||||||
|
// his user info.
|
||||||
|
await app.toggleRoomInfoPanel();
|
||||||
|
const rightPanel = page.locator(".mx_RightPanel");
|
||||||
|
await rightPanel.getByRole("menuitem", { name: "People" }).click();
|
||||||
|
await rightPanel.getByRole("button", { name: bob.credentials!.userId }).click();
|
||||||
|
await expect(rightPanel.locator(".mx_UserInfo_devices")).toContainText("1 session");
|
||||||
|
|
||||||
// Our app is blocked from syncing while Bob sends his messages.
|
// Our app is blocked from syncing while Bob sends his messages.
|
||||||
await app.client.network.goOffline();
|
await app.client.network.goOffline();
|
||||||
|
|
||||||
@@ -305,7 +320,50 @@ test.describe("Cryptography", function () {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const penultimate = page.locator(".mx_EventTile").filter({ hasText: "test encrypted from verified" });
|
const penultimate = page.locator(".mx_EventTile").filter({ hasText: "test encrypted from verified" });
|
||||||
await expect(penultimate.locator(".mx_EventTile_e2eIcon")).not.toBeVisible();
|
await assertNoE2EIcon(penultimate, app);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should show correct shields on events sent by users with changed identity", async ({
|
||||||
|
page,
|
||||||
|
app,
|
||||||
|
bot: bob,
|
||||||
|
homeserver,
|
||||||
|
}) => {
|
||||||
|
// Verify Bob
|
||||||
|
await verify(app, bob);
|
||||||
|
|
||||||
|
// Bob logs in a new device and resets cross-signing
|
||||||
|
const bobSecondDevice = await createSecondBotDevice(page, homeserver, bob);
|
||||||
|
await bootstrapCrossSigningForClient(await bobSecondDevice.prepareClient(), bob.credentials, true);
|
||||||
|
|
||||||
|
/* should show an error for a message from a previously verified device */
|
||||||
|
await bobSecondDevice.sendMessage(testRoomId, "test encrypted from user that was previously verified");
|
||||||
|
const last = page.locator(".mx_EventTile_last");
|
||||||
|
await expect(last).toContainText("test encrypted from user that was previously verified");
|
||||||
|
const lastE2eIcon = last.locator(".mx_EventTile_e2eIcon");
|
||||||
|
await expect(lastE2eIcon).toHaveClass(/mx_EventTile_e2eIcon_warning/);
|
||||||
|
await lastE2eIcon.focus();
|
||||||
|
await expect(await app.getTooltipForElement(lastE2eIcon)).toContainText(
|
||||||
|
"Sender's verified identity has changed",
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that the given message doesn't have an E2E warning icon.
|
||||||
|
*
|
||||||
|
* If it does, throw an error.
|
||||||
|
*/
|
||||||
|
async function assertNoE2EIcon(messageLocator: Locator, app: ElementAppPage) {
|
||||||
|
// Make sure the message itself exists, before we check if it has any icons
|
||||||
|
await messageLocator.waitFor();
|
||||||
|
|
||||||
|
const e2eIcon = messageLocator.locator(".mx_EventTile_e2eIcon");
|
||||||
|
if ((await e2eIcon.count()) > 0) {
|
||||||
|
// uh-oh, there is an e2e icon. Let's find out what it's about so that we can throw a helpful error.
|
||||||
|
await e2eIcon.focus();
|
||||||
|
const tooltip = await app.getTooltipForElement(e2eIcon);
|
||||||
|
throw new Error(`Found an unexpected e2eIcon with tooltip '${await tooltip.textContent()}'`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2024 New Vector Ltd.
|
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.
|
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 */
|
/** Tests for the "invisible crypto" behaviour -- i.e., when the "exclude insecure devices" setting is enabled */
|
||||||
test.describe("Invisible cryptography", () => {
|
test.describe("Invisible cryptography", () => {
|
||||||
|
test.slow();
|
||||||
test.use({
|
test.use({
|
||||||
displayName: "Alice",
|
displayName: "Alice",
|
||||||
botCreateOpts: { displayName: "Bob" },
|
botCreateOpts: { displayName: "Bob" },
|
||||||
@@ -51,6 +52,6 @@ test.describe("Invisible cryptography", () => {
|
|||||||
/* should show an error for a message from a previously verified device */
|
/* should show an error for a message from a previously verified device */
|
||||||
await bobSecondDevice.sendMessage(testRoomId, "test encrypted from user that was previously verified");
|
await bobSecondDevice.sendMessage(testRoomId, "test encrypted from user that was previously verified");
|
||||||
const lastTile = page.locator(".mx_EventTile_last");
|
const lastTile = page.locator(".mx_EventTile_last");
|
||||||
await expect(lastTile).toContainText("Verified identity has changed");
|
await expect(lastTile).toContainText("Sender's verified identity has changed");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,16 +2,18 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2024 The Matrix.org Foundation C.I.C.
|
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.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { test, expect } from "../../element-web-test";
|
import { test, expect } from "../../element-web-test";
|
||||||
import { createRoom, enableKeyBackup, logIntoElement, sendMessageInCurrentRoom } from "./utils";
|
import { createRoom, enableKeyBackup, logIntoElement, sendMessageInCurrentRoom } from "./utils";
|
||||||
|
import { isDendrite } from "../../plugins/homeserver/dendrite";
|
||||||
|
|
||||||
test.describe("Logout tests", () => {
|
test.describe("Logout tests", () => {
|
||||||
|
test.skip(isDendrite, "Dendrite lacks support for MSC3967 so requires additional auth here");
|
||||||
test.beforeEach(async ({ page, homeserver, credentials }) => {
|
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 }) => {
|
test("Ask to set up recovery on logout if not setup", async ({ page, app }) => {
|
||||||
|
|||||||
@@ -2,16 +2,19 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
|
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.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { readFile } from "node:fs/promises";
|
import { readFile } from "node:fs/promises";
|
||||||
|
|
||||||
import { expect, test as base } from "../../element-web-test";
|
import { expect, test } from "../../element-web-test";
|
||||||
|
|
||||||
|
test.describe("migration", { tag: "@no-webkit" }, function () {
|
||||||
|
test.use({
|
||||||
|
displayName: "Alice",
|
||||||
|
|
||||||
const test = base.extend({
|
|
||||||
// Replace the `user` fixture with one which populates the indexeddb data before starting the app.
|
// Replace the `user` fixture with one which populates the indexeddb data before starting the app.
|
||||||
user: async ({ context, pageWithCredentials: page, credentials }, use) => {
|
user: async ({ context, pageWithCredentials: page, credentials }, use) => {
|
||||||
await page.route(`/test_indexeddb_cryptostore_dump/*`, async (route, request) => {
|
await page.route(`/test_indexeddb_cryptostore_dump/*`, async (route, request) => {
|
||||||
@@ -25,11 +28,7 @@ const test = base.extend({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
test.describe("migration", function () {
|
|
||||||
test.use({ displayName: "Alice" });
|
|
||||||
|
|
||||||
test("Should support migration from legacy crypto", async ({ context, user, page }, workerInfo) => {
|
test("Should support migration from legacy crypto", async ({ context, user, page }, workerInfo) => {
|
||||||
test.skip(workerInfo.project.name === "Legacy Crypto", "This test only works with Rust crypto.");
|
|
||||||
test.slow();
|
test.slow();
|
||||||
|
|
||||||
// We should see a migration progress bar
|
// We should see a migration progress bar
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
|
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.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -2,12 +2,13 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
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.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { type Preset, type Visibility } from "matrix-js-sdk/src/matrix";
|
import { type Preset, type Visibility } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
|
import type { Page } from "@playwright/test";
|
||||||
import { test, expect } from "../../element-web-test";
|
import { test, expect } from "../../element-web-test";
|
||||||
import { doTwoWaySasVerification, awaitVerifier } from "./utils";
|
import { doTwoWaySasVerification, awaitVerifier } from "./utils";
|
||||||
import { Client } from "../../pages/client";
|
import { Client } from "../../pages/client";
|
||||||
@@ -38,6 +39,8 @@ test.describe("User verification", () => {
|
|||||||
toasts,
|
toasts,
|
||||||
room: { roomId: dmRoomId },
|
room: { roomId: dmRoomId },
|
||||||
}) => {
|
}) => {
|
||||||
|
await waitForDeviceKeys(page);
|
||||||
|
|
||||||
// once Alice has joined, Bob starts the verification
|
// once Alice has joined, Bob starts the verification
|
||||||
const bobVerificationRequest = await bob.evaluateHandle(
|
const bobVerificationRequest = await bob.evaluateHandle(
|
||||||
async (client, { dmRoomId, aliceCredentials }) => {
|
async (client, { dmRoomId, aliceCredentials }) => {
|
||||||
@@ -71,7 +74,7 @@ test.describe("User verification", () => {
|
|||||||
/* on the bot side, wait for the verifier to exist ... */
|
/* on the bot side, wait for the verifier to exist ... */
|
||||||
const botVerifier = await awaitVerifier(bobVerificationRequest);
|
const botVerifier = await awaitVerifier(bobVerificationRequest);
|
||||||
// ... confirm ...
|
// ... confirm ...
|
||||||
botVerifier.evaluate((verifier) => verifier.verify());
|
void botVerifier.evaluate((verifier) => verifier.verify());
|
||||||
// ... and then check the emoji match
|
// ... and then check the emoji match
|
||||||
await doTwoWaySasVerification(page, botVerifier);
|
await doTwoWaySasVerification(page, botVerifier);
|
||||||
|
|
||||||
@@ -87,6 +90,8 @@ test.describe("User verification", () => {
|
|||||||
toasts,
|
toasts,
|
||||||
room: { roomId: dmRoomId },
|
room: { roomId: dmRoomId },
|
||||||
}) => {
|
}) => {
|
||||||
|
await waitForDeviceKeys(page);
|
||||||
|
|
||||||
// once Alice has joined, Bob starts the verification
|
// once Alice has joined, Bob starts the verification
|
||||||
const bobVerificationRequest = await bob.evaluateHandle(
|
const bobVerificationRequest = await bob.evaluateHandle(
|
||||||
async (client, { dmRoomId, aliceCredentials }) => {
|
async (client, { dmRoomId, aliceCredentials }) => {
|
||||||
@@ -149,3 +154,15 @@ async function createDMRoom(client: Client, userId: string): Promise<string> {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait until we get the other user's device keys.
|
||||||
|
* In newer rust-crypto versions, the verification request will be ignored if we
|
||||||
|
* don't have the sender's device keys.
|
||||||
|
*/
|
||||||
|
async function waitForDeviceKeys(page: Page): Promise<void> {
|
||||||
|
await expect(page.getByRole("button", { name: "Avatar" })).toBeVisible();
|
||||||
|
const avatar = await page.getByRole("button", { name: "Avatar" });
|
||||||
|
await avatar.click();
|
||||||
|
await expect(page.getByText("1 session")).toBeVisible();
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
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.
|
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 {
|
import type {
|
||||||
CryptoEvent,
|
CryptoEvent,
|
||||||
EmojiMapping,
|
EmojiMapping,
|
||||||
|
GeneratedSecretStorageKey,
|
||||||
ShowSasCallbacks,
|
ShowSasCallbacks,
|
||||||
VerificationRequest,
|
VerificationRequest,
|
||||||
Verifier,
|
Verifier,
|
||||||
@@ -22,6 +23,46 @@ import { Client } from "../../pages/client";
|
|||||||
import { ElementAppPage } from "../../pages/ElementAppPage";
|
import { ElementAppPage } from "../../pages/ElementAppPage";
|
||||||
import { Bot } from "../../pages/bot";
|
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
|
* 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) => {
|
return new Promise<EmojiMapping[]>((resolve) => {
|
||||||
const onShowSas = (event: ShowSasCallbacks) => {
|
const onShowSas = (event: ShowSasCallbacks) => {
|
||||||
verifier.off("show_sas" as VerifierEvent, onShowSas);
|
verifier.off("show_sas" as VerifierEvent, onShowSas);
|
||||||
event.confirm();
|
void event.confirm();
|
||||||
resolve(event.sas.emoji);
|
resolve(event.sas.emoji);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -138,22 +179,9 @@ export async function checkDeviceIsConnectedKeyBackup(
|
|||||||
*
|
*
|
||||||
* If a `securityKey` is given, verifies the new device using the key.
|
* If a `securityKey` is given, verifies the new device using the key.
|
||||||
*/
|
*/
|
||||||
export async function logIntoElement(
|
export async function logIntoElement(page: Page, credentials: Credentials, securityKey?: string) {
|
||||||
page: Page,
|
|
||||||
homeserver: HomeserverInstance,
|
|
||||||
credentials: Credentials,
|
|
||||||
securityKey?: string,
|
|
||||||
) {
|
|
||||||
await page.goto("/#/login");
|
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.getByRole("textbox", { name: "Username" }).fill(credentials.userId);
|
||||||
await page.getByPlaceholder("Password").fill(credentials.password);
|
await page.getByPlaceholder("Password").fill(credentials.password);
|
||||||
await page.getByRole("button", { name: "Sign in" }).click();
|
await page.getByRole("button", { name: "Sign in" }).click();
|
||||||
@@ -220,11 +248,7 @@ export async function doTwoWaySasVerification(page: Page, verifier: JSHandle<Ver
|
|||||||
for (let i = 0; i < emojis.length; i++) {
|
for (let i = 0; i < emojis.length; i++) {
|
||||||
const emoji = emojis[i];
|
const emoji = emojis[i];
|
||||||
const emojiBlock = emojiBlocks.nth(i);
|
const emojiBlock = emojiBlocks.nth(i);
|
||||||
const textContent = await emojiBlock.textContent();
|
await expect(emojiBlock).toHaveText(emoji[0] + emoji[1]);
|
||||||
// VerificationShowSas munges the case of the emoji descriptions returned by the js-sdk before
|
|
||||||
// displaying them. Once we drop support for legacy crypto, that code can go away, and so can the
|
|
||||||
// case-munging here.
|
|
||||||
expect(textContent.toLowerCase()).toEqual(emoji[0] + emoji[1].toLowerCase());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,7 +354,7 @@ export async function autoJoin(client: Client) {
|
|||||||
await client.evaluate((cli) => {
|
await client.evaluate((cli) => {
|
||||||
cli.on(window.matrixcs.RoomMemberEvent.Membership, (event, member) => {
|
cli.on(window.matrixcs.RoomMemberEvent.Membership, (event, member) => {
|
||||||
if (member.membership === "invite" && member.userId === cli.getUserId()) {
|
if (member.membership === "invite" && member.userId === cli.getUserId()) {
|
||||||
cli.joinRoom(member.roomId);
|
void cli.joinRoom(member.roomId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -389,3 +413,25 @@ export async function createSecondBotDevice(page: Page, homeserver: HomeserverIn
|
|||||||
await bobSecondDevice.prepareClient();
|
await bobSecondDevice.prepareClient();
|
||||||
return bobSecondDevice;
|
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
39
playwright/e2e/csAPI.ts
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
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.
|
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 { expect, test } from "../../element-web-test";
|
||||||
import { ElementAppPage } from "../../pages/ElementAppPage";
|
import { ElementAppPage } from "../../pages/ElementAppPage";
|
||||||
import { SettingLevel } from "../../../src/settings/SettingLevel";
|
import { SettingLevel } from "../../../src/settings/SettingLevel";
|
||||||
|
import { isDendrite } from "../../plugins/homeserver/dendrite";
|
||||||
|
|
||||||
async function sendEvent(app: ElementAppPage, roomId: string): Promise<ISendEventResponse> {
|
async function sendEvent(app: ElementAppPage, roomId: string): Promise<ISendEventResponse> {
|
||||||
return app.client.sendEvent(roomId, null, "m.room.message" as EventType, {
|
return app.client.sendEvent(roomId, null, "m.room.message" as EventType, {
|
||||||
@@ -31,6 +32,8 @@ function mkPadding(n: number): IContent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test.describe("Editing", () => {
|
test.describe("Editing", () => {
|
||||||
|
test.skip(isDendrite, "due to a Dendrite bug https://github.com/element-hq/dendrite/issues/3488");
|
||||||
|
|
||||||
// Edit "Message"
|
// Edit "Message"
|
||||||
const editLastMessage = async (page: Page, edit: string) => {
|
const editLastMessage = async (page: Page, edit: string) => {
|
||||||
const eventTile = page.locator(".mx_RoomView_MessageList .mx_EventTile_last");
|
const eventTile = page.locator(".mx_RoomView_MessageList .mx_EventTile_last");
|
||||||
@@ -66,7 +69,10 @@ test.describe("Editing", () => {
|
|||||||
botCreateOpts: { displayName: "Bob" },
|
botCreateOpts: { displayName: "Bob" },
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should render and interact with the message edit history dialog", async ({ page, user, app, room }) => {
|
test(
|
||||||
|
"should render and interact with the message edit history dialog",
|
||||||
|
{ tag: "@screenshot" },
|
||||||
|
async ({ page, user, app, room }) => {
|
||||||
// Click the "Remove" button on the message edit history dialog
|
// Click the "Remove" button on the message edit history dialog
|
||||||
const clickButtonRemove = async (locator: Locator) => {
|
const clickButtonRemove = async (locator: Locator) => {
|
||||||
const eventTileLine = locator.locator(".mx_EventTile_line");
|
const eventTileLine = locator.locator(".mx_EventTile_line");
|
||||||
@@ -185,7 +191,8 @@ test.describe("Editing", () => {
|
|||||||
.locator(".mx_RoomView_MessageList")
|
.locator(".mx_RoomView_MessageList")
|
||||||
.locator(".mx_EventTile_last .mx_RedactedBody", { hasText: "Message deleted" }),
|
.locator(".mx_EventTile_last .mx_RedactedBody", { hasText: "Message deleted" }),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
test("should render 'View Source' button in developer mode on the message edit history dialog", async ({
|
test("should render 'View Source' button in developer mode on the message edit history dialog", async ({
|
||||||
page,
|
page,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2024 The Matrix.org Foundation C.I.C.
|
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.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ test.describe("Image Upload", () => {
|
|||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should show image preview when uploading an image", async ({ page, app }) => {
|
test("should show image preview when uploading an image", { tag: "@screenshot" }, async ({ page, app }) => {
|
||||||
await page
|
await page
|
||||||
.locator(".mx_MessageComposer_actions input[type='file']")
|
.locator(".mx_MessageComposer_actions input[type='file']")
|
||||||
.setInputFiles("playwright/sample-files/riot.png");
|
.setInputFiles("playwright/sample-files/riot.png");
|
||||||
|
|||||||
@@ -2,52 +2,70 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2024 The Matrix.org Foundation C.I.C.
|
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.
|
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 { 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 email = "user@nowhere.dummy";
|
||||||
|
|
||||||
test.describe("Forgot Password", () => {
|
const test = base.extend<{ credentials: Pick<Credentials, "username" | "password"> }>({
|
||||||
test.use({
|
// eslint-disable-next-line no-empty-pattern
|
||||||
startHomeserverOpts: ({ mailhog }, use) =>
|
credentials: async ({}, use, testInfo) => {
|
||||||
use({
|
await use({
|
||||||
template: "email",
|
username: `user_${testInfo.testId}`,
|
||||||
variables: {
|
// this has to be password-like enough to please zxcvbn. Needless to say it's just from pwgen.
|
||||||
SMTP_HOST: "host.containers.internal",
|
password: "oETo7MPf0o",
|
||||||
SMTP_PORT: mailhog.instance.smtpPort,
|
});
|
||||||
},
|
},
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("renders properly", async ({ page, homeserver }) => {
|
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.skip(isDendrite, "not yet wired up");
|
||||||
|
|
||||||
|
test("renders properly", { tag: "@screenshot" }, async ({ page, homeserver }) => {
|
||||||
await page.goto("/");
|
await page.goto("/");
|
||||||
|
|
||||||
await page.getByRole("link", { name: "Sign in" }).click();
|
await page.getByRole("link", { name: "Sign in" }).click();
|
||||||
|
|
||||||
// need to select a homeserver at this stage, before entering the forgot password flow
|
// 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 page.getByRole("button", { name: "Forgot password?" }).click();
|
||||||
|
|
||||||
await expect(page.getByRole("main")).toMatchScreenshot("forgot-password.png");
|
await expect(page.getByRole("main")).toMatchScreenshot("forgot-password.png");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("renders email verification dialog properly", async ({ page, homeserver }) => {
|
test(
|
||||||
const user = await homeserver.registerUser(username, password);
|
"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 page.getByRole("link", { name: "Sign in" }).click();
|
||||||
await selectHomeserver(page, homeserver.config.baseUrl);
|
await selectHomeserver(page, homeserver.baseUrl);
|
||||||
|
|
||||||
await page.getByRole("button", { name: "Forgot password?" }).click();
|
await page.getByRole("button", { name: "Forgot password?" }).click();
|
||||||
|
|
||||||
@@ -57,13 +75,14 @@ test.describe("Forgot Password", () => {
|
|||||||
|
|
||||||
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: "New Password", exact: true }).fill(credentials.password);
|
||||||
await page.getByRole("textbox", { name: "Confirm new password", exact: true }).fill(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");
|
||||||
});
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,13 +2,14 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
|
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.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Page } from "@playwright/test";
|
import type { Page } from "@playwright/test";
|
||||||
import { test, expect } from "../../element-web-test";
|
import { test, expect } from "../../element-web-test";
|
||||||
import { openIntegrationManager } from "./utils";
|
import { openIntegrationManager } from "./utils";
|
||||||
|
import type { UserWidget } from "../../../src/utils/WidgetUtils-types.ts";
|
||||||
|
|
||||||
const ROOM_NAME = "Integration Manager Test";
|
const ROOM_NAME = "Integration Manager Test";
|
||||||
|
|
||||||
@@ -92,7 +93,7 @@ test.describe("Integration Manager: Get OpenID Token", () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
id: "integration-manager",
|
id: "integration-manager",
|
||||||
},
|
} as unknown as UserWidget,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Succeed when checking the token is valid
|
// Succeed when checking the token is valid
|
||||||
|
|||||||
@@ -2,13 +2,14 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
|
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.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Page } from "@playwright/test";
|
import type { Page } from "@playwright/test";
|
||||||
import { test, expect } from "../../element-web-test";
|
import { test, expect } from "../../element-web-test";
|
||||||
import { openIntegrationManager } from "./utils";
|
import { openIntegrationManager } from "./utils";
|
||||||
|
import type { UserWidget } from "../../../src/utils/WidgetUtils-types.ts";
|
||||||
|
|
||||||
const ROOM_NAME = "Integration Manager Test";
|
const ROOM_NAME = "Integration Manager Test";
|
||||||
const USER_DISPLAY_NAME = "Alice";
|
const USER_DISPLAY_NAME = "Alice";
|
||||||
@@ -68,29 +69,13 @@ async function sendActionFromIntegrationManager(
|
|||||||
await iframe.getByRole("button", { name: "Press to send action" }).click();
|
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) {
|
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
|
await expect(async () => {
|
||||||
// This is quite horrible but seems the most stable way of clicking 0-N buttons,
|
await page.locator(".mx_GenericEventListSummary_toggle[aria-expanded=false]").last().click();
|
||||||
// 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({
|
await expect(page.getByText(`${USER_DISPLAY_NAME} removed ${BOT_DISPLAY_NAME}: ${KICK_REASON}`)).toBeVisible({
|
||||||
visible: shouldExist,
|
visible: shouldExist,
|
||||||
});
|
});
|
||||||
|
}).toPass();
|
||||||
}
|
}
|
||||||
|
|
||||||
test.describe("Integration Manager: Kick", () => {
|
test.describe("Integration Manager: Kick", () => {
|
||||||
@@ -136,7 +121,7 @@ test.describe("Integration Manager: Kick", () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
id: "integration-manager",
|
id: "integration-manager",
|
||||||
},
|
} as unknown as UserWidget,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Succeed when checking the token is valid
|
// Succeed when checking the token is valid
|
||||||
|
|||||||
@@ -2,13 +2,14 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
|
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.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Page } from "@playwright/test";
|
import type { Page } from "@playwright/test";
|
||||||
import { test, expect } from "../../element-web-test";
|
import { test, expect } from "../../element-web-test";
|
||||||
import { openIntegrationManager } from "./utils";
|
import { openIntegrationManager } from "./utils";
|
||||||
|
import type { UserWidget } from "../../../src/utils/WidgetUtils-types.ts";
|
||||||
|
|
||||||
const ROOM_NAME = "Integration Manager Test";
|
const ROOM_NAME = "Integration Manager Test";
|
||||||
|
|
||||||
@@ -107,7 +108,7 @@ test.describe("Integration Manager: Read Events", () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
id: "integration-manager",
|
id: "integration-manager",
|
||||||
},
|
} as unknown as UserWidget,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Succeed when checking the token is valid
|
// Succeed when checking the token is valid
|
||||||
|
|||||||
@@ -2,13 +2,14 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
|
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.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Page } from "@playwright/test";
|
import type { Page } from "@playwright/test";
|
||||||
import { test, expect } from "../../element-web-test";
|
import { test, expect } from "../../element-web-test";
|
||||||
import { openIntegrationManager } from "./utils";
|
import { openIntegrationManager } from "./utils";
|
||||||
|
import type { UserWidget } from "../../../src/utils/WidgetUtils-types.ts";
|
||||||
|
|
||||||
const ROOM_NAME = "Integration Manager Test";
|
const ROOM_NAME = "Integration Manager Test";
|
||||||
|
|
||||||
@@ -113,7 +114,7 @@ test.describe("Integration Manager: Send Event", () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
id: "integration-manager",
|
id: "integration-manager",
|
||||||
},
|
} as unknown as UserWidget,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Succeed when checking the token is valid
|
// Succeed when checking the token is valid
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
|
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.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ Copyright 2024 New Vector Ltd.
|
|||||||
Copyright 2023 Suguru Hirahara
|
Copyright 2023 Suguru Hirahara
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
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.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ test.describe("Invite dialog", function () {
|
|||||||
|
|
||||||
const botName = "BotAlice";
|
const botName = "BotAlice";
|
||||||
|
|
||||||
test("should support inviting a user to a room", async ({ page, app, user, bot }) => {
|
test("should support inviting a user to a room", { tag: "@screenshot" }, async ({ page, app, user, bot }) => {
|
||||||
// Create and view a room
|
// Create and view a room
|
||||||
await app.client.createRoom({ name: "Test Room" });
|
await app.client.createRoom({ name: "Test Room" });
|
||||||
await app.viewRoomByName("Test Room");
|
await app.viewRoomByName("Test Room");
|
||||||
@@ -73,12 +73,17 @@ test.describe("Invite dialog", function () {
|
|||||||
await expect(page.getByText(`${botName} joined the room`)).toBeVisible();
|
await expect(page.getByText(`${botName} joined the room`)).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should support inviting a user to Direct Messages", async ({ page, app, user, bot }) => {
|
test(
|
||||||
|
"should support inviting a user to Direct Messages",
|
||||||
|
{ tag: "@screenshot" },
|
||||||
|
async ({ page, app, user, bot }) => {
|
||||||
await page.locator(".mx_RoomList").getByRole("button", { name: "Start chat" }).click();
|
await page.locator(".mx_RoomList").getByRole("button", { name: "Start chat" }).click();
|
||||||
|
|
||||||
const other = page.locator(".mx_InviteDialog_other");
|
const other = page.locator(".mx_InviteDialog_other");
|
||||||
// Assert that the header is rendered
|
// Assert that the header is rendered
|
||||||
await expect(other.locator(".mx_Dialog_header .mx_Dialog_title").getByText("Direct Messages")).toBeVisible();
|
await expect(
|
||||||
|
other.locator(".mx_Dialog_header .mx_Dialog_title").getByText("Direct Messages"),
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
// Assert that the bar is rendered
|
// Assert that the bar is rendered
|
||||||
await expect(other.locator(".mx_InviteDialog_addressBar")).toBeVisible();
|
await expect(other.locator(".mx_InviteDialog_addressBar")).toBeVisible();
|
||||||
@@ -88,7 +93,9 @@ test.describe("Invite dialog", function () {
|
|||||||
|
|
||||||
await other.getByTestId("invite-dialog-input").fill(bot.credentials.userId);
|
await other.getByTestId("invite-dialog-input").fill(bot.credentials.userId);
|
||||||
|
|
||||||
await expect(other.locator(".mx_InviteDialog_tile_nameStack").getByText(bot.credentials.userId)).toBeVisible();
|
await expect(
|
||||||
|
other.locator(".mx_InviteDialog_tile_nameStack").getByText(bot.credentials.userId),
|
||||||
|
).toBeVisible();
|
||||||
await other.locator(".mx_InviteDialog_tile_nameStack").getByText(botName).click();
|
await other.locator(".mx_InviteDialog_tile_nameStack").getByText(botName).click();
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
@@ -108,7 +115,10 @@ test.describe("Invite dialog", function () {
|
|||||||
// TODO: implement the test on room-header.spec.ts
|
// TODO: implement the test on room-header.spec.ts
|
||||||
const roomHeader = page.locator(".mx_RoomHeader");
|
const roomHeader = page.locator(".mx_RoomHeader");
|
||||||
await roomHeader.locator(".mx_RoomHeader_heading").hover();
|
await roomHeader.locator(".mx_RoomHeader_heading").hover();
|
||||||
await expect(roomHeader.locator(".mx_RoomHeader_heading")).toHaveCSS("background-color", "rgba(0, 0, 0, 0)");
|
await expect(roomHeader.locator(".mx_RoomHeader_heading")).toHaveCSS(
|
||||||
|
"background-color",
|
||||||
|
"rgba(0, 0, 0, 0)",
|
||||||
|
);
|
||||||
|
|
||||||
// Send a message to invite the bots
|
// Send a message to invite the bots
|
||||||
const composer = app.getComposer().locator("[contenteditable]");
|
const composer = app.getComposer().locator("[contenteditable]");
|
||||||
@@ -120,5 +130,6 @@ test.describe("Invite dialog", function () {
|
|||||||
|
|
||||||
// Assert that the message is displayed at the bottom
|
// Assert that the message is displayed at the bottom
|
||||||
await expect(page.locator(".mx_EventTile_last").getByText("Hello")).toBeVisible();
|
await expect(page.locator(".mx_EventTile_last").getByText("Hello")).toBeVisible();
|
||||||
});
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,15 +2,17 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
|
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.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { test, expect } from "../../element-web-test";
|
import { test, expect } from "../../element-web-test";
|
||||||
import { waitForRoom } from "../utils";
|
import { waitForRoom } from "../utils";
|
||||||
import { Filter } from "../../pages/Spotlight";
|
import { Filter } from "../../pages/Spotlight";
|
||||||
|
import { isDendrite } from "../../plugins/homeserver/dendrite";
|
||||||
|
|
||||||
test.describe("Create Knock Room", () => {
|
test.describe("Create Knock Room", () => {
|
||||||
|
test.skip(isDendrite, "Dendrite does not have support for knocking");
|
||||||
test.use({
|
test.use({
|
||||||
displayName: "Alice",
|
displayName: "Alice",
|
||||||
labsFlags: ["feature_ask_to_join"],
|
labsFlags: ["feature_ask_to_join"],
|
||||||
@@ -79,6 +81,7 @@ test.describe("Create Knock Room", () => {
|
|||||||
|
|
||||||
const spotlightDialog = await app.openSpotlight();
|
const spotlightDialog = await app.openSpotlight();
|
||||||
await spotlightDialog.filter(Filter.PublicRooms);
|
await spotlightDialog.filter(Filter.PublicRooms);
|
||||||
|
await spotlightDialog.search("Cyber");
|
||||||
await expect(spotlightDialog.results.nth(0)).toContainText("Cybersecurity");
|
await expect(spotlightDialog.results.nth(0)).toContainText("Cybersecurity");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user