Compare commits
104 Commits
t3chguy/mo
...
v1.11.111
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7c0e91fdc | ||
|
|
fc06cf1276 | ||
|
|
4f702b70aa | ||
|
|
9f15532d12 | ||
|
|
71cf19f4b2 | ||
|
|
1925132a3c | ||
|
|
8fa3d7e4b7 | ||
|
|
1b4a979b6c | ||
|
|
d287ac07a3 | ||
|
|
8903927e0c | ||
|
|
4d48d1b2f2 | ||
|
|
f75d41054f | ||
|
|
701019052c | ||
|
|
cf692e751b | ||
|
|
1a005ad5d2 | ||
|
|
42f7bc1d0d | ||
|
|
b7f89db43c | ||
|
|
98a04e1812 | ||
|
|
42d726a4ff | ||
|
|
b6f5843028 | ||
|
|
81d054bb99 | ||
|
|
a1f56ebbf2 | ||
|
|
a003ebcb35 | ||
|
|
87b4918d34 | ||
|
|
c6f47cfd8e | ||
|
|
a112dfe1db | ||
|
|
4b4cb896eb | ||
|
|
6a1c0502aa | ||
|
|
ea5e525133 | ||
|
|
14d16364db | ||
|
|
67e0ecc454 | ||
|
|
427cddb8e5 | ||
|
|
df9dfaf16f | ||
|
|
9b5410bad5 | ||
|
|
afab82068d | ||
|
|
e8c88918cb | ||
|
|
c842b615db | ||
|
|
aab1fae299 | ||
|
|
ef3a6a9429 | ||
|
|
5c8c39424a | ||
|
|
4735412c91 | ||
|
|
4b6e5d380e | ||
|
|
1f825f11de | ||
|
|
646162db4e | ||
|
|
2c6f349ce7 | ||
|
|
fffe7a31be | ||
|
|
002e4f6655 | ||
|
|
260042b388 | ||
|
|
6e78d739ac | ||
|
|
78bf5644a0 | ||
|
|
96cc35a68c | ||
|
|
0f93481266 | ||
|
|
433eb23d88 | ||
|
|
3df8293085 | ||
|
|
76674e43b3 | ||
|
|
0bc6fa9f6e | ||
|
|
fd6e8054a7 | ||
|
|
de4f72fac0 | ||
|
|
f5d6f8f639 | ||
|
|
cc20136170 | ||
|
|
29f6cc03bd | ||
|
|
8f91f8fac5 | ||
|
|
8d3ea2b71b | ||
|
|
aa5bdab3ba | ||
|
|
08ec6166c7 | ||
|
|
362c7d2aac | ||
|
|
0c498a66b1 | ||
|
|
64dfbc5aa5 | ||
|
|
12dbe719d7 | ||
|
|
ee6ce8ac1d | ||
|
|
31506ef864 | ||
|
|
713f524948 | ||
|
|
e880a866ed | ||
|
|
789dba7b3d | ||
|
|
76be5ccc9e | ||
|
|
3b675b83f1 | ||
|
|
8bd98aa3fd | ||
|
|
b897006899 | ||
|
|
01c4ba8893 | ||
|
|
001ed616f6 | ||
|
|
2395cb1402 | ||
|
|
7951e48291 | ||
|
|
664f79306a | ||
|
|
29e895095f | ||
|
|
0d3a81ee8f | ||
|
|
26e24624d9 | ||
|
|
e94d690587 | ||
|
|
4abdb74673 | ||
|
|
59531ea512 | ||
|
|
4da27eb199 | ||
|
|
d2e4631a14 | ||
|
|
6ff71480d8 | ||
|
|
700068a558 | ||
|
|
d5a9b3f4c0 | ||
|
|
bbb179b6d3 | ||
|
|
93095f99db | ||
|
|
4d3fde192d | ||
|
|
bcf755d45f | ||
|
|
adfa43dcbb | ||
|
|
96dbddcb14 | ||
|
|
227c8ff1cd | ||
|
|
619e11a749 | ||
|
|
9590e59fd2 | ||
|
|
745c12f10d |
3
.github/CODEOWNERS
vendored
@@ -20,6 +20,7 @@
|
|||||||
# 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
|
/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 & mas plugins as this is updated by GHA for docker image updating
|
||||||
/playwright/testcontainers/synapse.ts
|
/playwright/testcontainers/synapse.ts
|
||||||
|
/playwright/testcontainers/mas.ts
|
||||||
|
|
||||||
|
|||||||
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
## Checklist
|
## Checklist
|
||||||
|
|
||||||
|
- [ ] I have read through [review guidelines](../docs/review.md) and [CONTRIBUTING.md](../CONTRIBUTING.md).
|
||||||
- [ ] Tests written for new code (and old code if feasible).
|
- [ ] Tests written for new code (and old code if feasible).
|
||||||
- [ ] New or updated `public`/`exported` symbols have accurate [TSDoc](https://tsdoc.org/) documentation.
|
- [ ] New or updated `public`/`exported` symbols have accurate [TSDoc](https://tsdoc.org/) documentation.
|
||||||
- [ ] Linter and other CI checks pass.
|
- [ ] Linter and other CI checks pass.
|
||||||
|
|||||||
2
.github/workflows/build.yml
vendored
@@ -43,7 +43,7 @@ jobs:
|
|||||||
run:
|
run:
|
||||||
shell: bash
|
shell: bash
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||||
|
|
||||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
2
.github/workflows/build_debian.yaml
vendored
@@ -14,7 +14,7 @@ jobs:
|
|||||||
R2_URL: ${{ vars.CF_R2_S3_API }}
|
R2_URL: ${{ vars.CF_R2_S3_API }}
|
||||||
VERSION: ${{ github.ref_name }}
|
VERSION: ${{ github.ref_name }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||||
|
|
||||||
- name: Download package
|
- name: Download package
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
2
.github/workflows/build_develop.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
|||||||
R2_URL: ${{ vars.CF_R2_S3_API }}
|
R2_URL: ${{ vars.CF_R2_S3_API }}
|
||||||
R2_PUBLIC_URL: "https://element-web-develop.element.io"
|
R2_PUBLIC_URL: "https://element-web-develop.element.io"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||||
|
|
||||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
2
.github/workflows/deploy.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
SITE: ${{ inputs.site || 'staging.element.io' }}
|
SITE: ${{ inputs.site || 'staging.element.io' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||||
|
|
||||||
- name: Load GPG key
|
- name: Load GPG key
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
2
.github/workflows/docker.yaml
vendored
@@ -20,7 +20,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
TEST_TAG: vectorim/element-web:test
|
TEST_TAG: vectorim/element-web:test
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||||
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
|
||||||
|
|
||||||
|
|||||||
6
.github/workflows/docs.yml
vendored
@@ -17,18 +17,18 @@ jobs:
|
|||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- name: Fetch element-desktop
|
- name: Fetch element-desktop
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||||
with:
|
with:
|
||||||
repository: element-hq/element-desktop
|
repository: element-hq/element-desktop
|
||||||
path: element-desktop
|
path: element-desktop
|
||||||
|
|
||||||
- name: Fetch element-web
|
- name: Fetch element-web
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||||
with:
|
with:
|
||||||
path: element-web
|
path: element-web
|
||||||
|
|
||||||
- name: Fetch matrix-js-sdk
|
- name: Fetch matrix-js-sdk
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||||
with:
|
with:
|
||||||
repository: matrix-org/matrix-js-sdk
|
repository: matrix-org/matrix-js-sdk
|
||||||
path: matrix-js-sdk
|
path: matrix-js-sdk
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ jobs:
|
|||||||
actions: read
|
actions: read
|
||||||
steps:
|
steps:
|
||||||
- name: Download HTML report
|
- name: Download HTML report
|
||||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run-id: ${{ github.event.workflow_run.id }}
|
run-id: ${{ github.event.workflow_run.id }}
|
||||||
|
|||||||
12
.github/workflows/end-to-end-tests.yaml
vendored
@@ -50,7 +50,7 @@ jobs:
|
|||||||
runners-matrix: ${{ steps.runner-vars.outputs.matrix }}
|
runners-matrix: ${{ steps.runner-vars.outputs.matrix }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||||
with:
|
with:
|
||||||
repository: element-hq/element-web
|
repository: element-hq/element-web
|
||||||
|
|
||||||
@@ -129,13 +129,13 @@ jobs:
|
|||||||
- runAllTests: false
|
- runAllTests: false
|
||||||
project: Pinecone
|
project: Pinecone
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
repository: element-hq/element-web
|
repository: element-hq/element-web
|
||||||
|
|
||||||
- name: 📥 Download artifact
|
- name: 📥 Download artifact
|
||||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5
|
||||||
with:
|
with:
|
||||||
name: webapp
|
name: webapp
|
||||||
path: webapp
|
path: webapp
|
||||||
@@ -154,7 +154,7 @@ jobs:
|
|||||||
run: echo "version=$(yarn list --pattern @playwright/test --depth=0 --json --non-interactive --no-progress | jq -r '.data.trees[].name')" >> $GITHUB_OUTPUT
|
run: echo "version=$(yarn list --pattern @playwright/test --depth=0 --json --non-interactive --no-progress | jq -r '.data.trees[].name')" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Cache playwright binaries
|
- name: Cache playwright binaries
|
||||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4
|
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4
|
||||||
id: playwright-cache
|
id: playwright-cache
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/ms-playwright
|
path: ~/.cache/ms-playwright
|
||||||
@@ -201,7 +201,7 @@ jobs:
|
|||||||
if: always()
|
if: always()
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||||
if: inputs.skip != true
|
if: inputs.skip != true
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
@@ -219,7 +219,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Download blob reports from GitHub Actions Artifacts
|
- name: Download blob reports from GitHub Actions Artifacts
|
||||||
if: inputs.skip != true
|
if: inputs.skip != true
|
||||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5
|
||||||
with:
|
with:
|
||||||
pattern: all-blob-reports-*
|
pattern: all-blob-reports-*
|
||||||
path: all-blob-reports
|
path: all-blob-reports
|
||||||
|
|||||||
2
.github/workflows/netlify.yaml
vendored
@@ -28,7 +28,7 @@ jobs:
|
|||||||
Exercise caution. Use test accounts.
|
Exercise caution. Use test accounts.
|
||||||
|
|
||||||
- name: 📥 Download artifact
|
- name: 📥 Download artifact
|
||||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run-id: ${{ github.event.workflow_run.id }}
|
run-id: ${{ github.event.workflow_run.id }}
|
||||||
|
|||||||
11
.github/workflows/playwright-image-updates.yaml
vendored
@@ -10,7 +10,7 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||||
|
|
||||||
- name: Update synapse image
|
- name: Update synapse image
|
||||||
run: |
|
run: |
|
||||||
@@ -21,6 +21,15 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
IMAGE: ghcr.io/element-hq/synapse:develop
|
IMAGE: ghcr.io/element-hq/synapse:develop
|
||||||
|
|
||||||
|
- name: Update MAS image
|
||||||
|
run: |
|
||||||
|
docker pull "$IMAGE"
|
||||||
|
INSPECT=$(docker inspect --format='{{index .RepoDigests 0}}' "$IMAGE")
|
||||||
|
DIGEST=${INSPECT#*@}
|
||||||
|
sed -i "s/const TAG.*/const TAG = \"main@$DIGEST\";/" playwright/testcontainers/mas.ts
|
||||||
|
env:
|
||||||
|
IMAGE: ghcr.io/element-hq/matrix-authentication-service:main
|
||||||
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
id: cpr
|
id: cpr
|
||||||
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7
|
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7
|
||||||
|
|||||||
6
.github/workflows/release_prepare.yml
vendored
@@ -41,7 +41,7 @@ jobs:
|
|||||||
REPOS: matrix-js-sdk element-web element-desktop
|
REPOS: matrix-js-sdk element-web element-desktop
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Element Desktop
|
- name: Checkout Element Desktop
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||||
if: inputs.element-desktop
|
if: inputs.element-desktop
|
||||||
with:
|
with:
|
||||||
repository: element-hq/element-desktop
|
repository: element-hq/element-desktop
|
||||||
@@ -51,7 +51,7 @@ jobs:
|
|||||||
fetch-tags: true
|
fetch-tags: true
|
||||||
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
- name: Checkout Element Web
|
- name: Checkout Element Web
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||||
if: inputs.element-web
|
if: inputs.element-web
|
||||||
with:
|
with:
|
||||||
repository: element-hq/element-web
|
repository: element-hq/element-web
|
||||||
@@ -61,7 +61,7 @@ jobs:
|
|||||||
fetch-tags: true
|
fetch-tags: true
|
||||||
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
- name: Checkout Matrix JS SDK
|
- name: Checkout Matrix JS SDK
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||||
if: inputs.matrix-js-sdk
|
if: inputs.matrix-js-sdk
|
||||||
with:
|
with:
|
||||||
repository: matrix-org/matrix-js-sdk
|
repository: matrix-org/matrix-js-sdk
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ jobs:
|
|||||||
run: "sudo apt-get install -y tree"
|
run: "sudo apt-get install -y tree"
|
||||||
|
|
||||||
- name: Download Diffs
|
- name: Download Diffs
|
||||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run-id: ${{ github.event.workflow_run.id }}
|
run-id: ${{ github.event.workflow_run.id }}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ jobs:
|
|||||||
issues: read
|
issues: read
|
||||||
pull-requests: read
|
pull-requests: read
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
repository: element-hq/element-web
|
repository: element-hq/element-web
|
||||||
@@ -39,7 +39,7 @@ jobs:
|
|||||||
run: echo "version=$(yarn list --pattern @playwright/test --depth=0 --json --non-interactive --no-progress | jq -r '.data.trees[].name')" >> $GITHUB_OUTPUT
|
run: echo "version=$(yarn list --pattern @playwright/test --depth=0 --json --non-interactive --no-progress | jq -r '.data.trees[].name')" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Cache playwright binaries
|
- name: Cache playwright binaries
|
||||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4
|
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4
|
||||||
id: playwright-cache
|
id: playwright-cache
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/ms-playwright
|
path: ~/.cache/ms-playwright
|
||||||
|
|||||||
13
.github/workflows/static_analysis.yaml
vendored
@@ -23,7 +23,7 @@ jobs:
|
|||||||
name: "Typescript Syntax Check"
|
name: "Typescript Syntax Check"
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||||
|
|
||||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||||
with:
|
with:
|
||||||
@@ -52,12 +52,13 @@ jobs:
|
|||||||
error|misconfigured
|
error|misconfigured
|
||||||
welcome_to_element
|
welcome_to_element
|
||||||
devtools|settings|elementCallUrl
|
devtools|settings|elementCallUrl
|
||||||
|
labs|sliding_sync_description
|
||||||
|
|
||||||
rethemendex_lint:
|
rethemendex_lint:
|
||||||
name: "Rethemendex Check"
|
name: "Rethemendex Check"
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||||
|
|
||||||
- run: ./res/css/rethemendex.sh
|
- run: ./res/css/rethemendex.sh
|
||||||
|
|
||||||
@@ -67,7 +68,7 @@ jobs:
|
|||||||
name: "ESLint"
|
name: "ESLint"
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||||
|
|
||||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||||
with:
|
with:
|
||||||
@@ -85,7 +86,7 @@ jobs:
|
|||||||
name: "Style Lint"
|
name: "Style Lint"
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||||
|
|
||||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||||
with:
|
with:
|
||||||
@@ -103,7 +104,7 @@ jobs:
|
|||||||
name: "Workflow Lint"
|
name: "Workflow Lint"
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||||
|
|
||||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||||
with:
|
with:
|
||||||
@@ -121,7 +122,7 @@ jobs:
|
|||||||
name: "Analyse Dead Code"
|
name: "Analyse Dead Code"
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||||
|
|
||||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
4
.github/workflows/tests.yml
vendored
@@ -39,7 +39,7 @@ jobs:
|
|||||||
runner: [1, 2]
|
runner: [1, 2]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||||
with:
|
with:
|
||||||
repository: ${{ inputs.matrix-js-sdk-sha && 'element-hq/element-web' || github.repository }}
|
repository: ${{ inputs.matrix-js-sdk-sha && 'element-hq/element-web' || github.repository }}
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ jobs:
|
|||||||
JS_SDK_GITHUB_BASE_REF: ${{ inputs.matrix-js-sdk-sha }}
|
JS_SDK_GITHUB_BASE_REF: ${{ inputs.matrix-js-sdk-sha }}
|
||||||
|
|
||||||
- name: Jest Cache
|
- name: Jest Cache
|
||||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4
|
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4
|
||||||
with:
|
with:
|
||||||
path: /tmp/jest_cache
|
path: /tmp/jest_cache
|
||||||
key: ${{ hashFiles('**/yarn.lock') }}
|
key: ${{ hashFiles('**/yarn.lock') }}
|
||||||
|
|||||||
2
.github/workflows/triage-assigned.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
|||||||
contains(github.event.issue.assignees.*.login, 'dbkr') ||
|
contains(github.event.issue.assignees.*.login, 'dbkr') ||
|
||||||
contains(github.event.issue.assignees.*.login, 'MidhunSureshR')
|
contains(github.event.issue.assignees.*.login, 'MidhunSureshR')
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/add-to-project@main
|
- uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2
|
||||||
with:
|
with:
|
||||||
project-url: https://github.com/orgs/element-hq/projects/67
|
project-url: https://github.com/orgs/element-hq/projects/67
|
||||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
|
|||||||
2
.github/workflows/triage-incoming.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
|||||||
automate-project-columns:
|
automate-project-columns:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/add-to-project@main
|
- uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2
|
||||||
with:
|
with:
|
||||||
project-url: https://github.com/orgs/element-hq/projects/120
|
project-url: https://github.com/orgs/element-hq/projects/120
|
||||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
|
|||||||
12
.github/workflows/triage-labelled.yml
vendored
@@ -112,7 +112,7 @@ jobs:
|
|||||||
contains(github.event.issue.labels.*.name, 'O-Frequent') ||
|
contains(github.event.issue.labels.*.name, 'O-Frequent') ||
|
||||||
contains(github.event.issue.labels.*.name, 'A11y'))
|
contains(github.event.issue.labels.*.name, 'A11y'))
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/add-to-project@main
|
- uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2
|
||||||
with:
|
with:
|
||||||
project-url: https://github.com/orgs/element-hq/projects/18
|
project-url: https://github.com/orgs/element-hq/projects/18
|
||||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
@@ -123,7 +123,7 @@ jobs:
|
|||||||
if: >
|
if: >
|
||||||
contains(github.event.issue.labels.*.name, 'X-Needs-Product')
|
contains(github.event.issue.labels.*.name, 'X-Needs-Product')
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/add-to-project@main
|
- uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2
|
||||||
with:
|
with:
|
||||||
project-url: https://github.com/orgs/element-hq/projects/28
|
project-url: https://github.com/orgs/element-hq/projects/28
|
||||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
@@ -134,7 +134,7 @@ jobs:
|
|||||||
if: >
|
if: >
|
||||||
contains(github.event.issue.labels.*.name, 'A-New-Search-Experience')
|
contains(github.event.issue.labels.*.name, 'A-New-Search-Experience')
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/add-to-project@main
|
- uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2
|
||||||
with:
|
with:
|
||||||
project-url: https://github.com/orgs/element-hq/projects/48
|
project-url: https://github.com/orgs/element-hq/projects/48
|
||||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
@@ -145,7 +145,7 @@ jobs:
|
|||||||
if: >
|
if: >
|
||||||
contains(github.event.issue.labels.*.name, 'Team: VoIP')
|
contains(github.event.issue.labels.*.name, 'Team: VoIP')
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/add-to-project@main
|
- uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2
|
||||||
with:
|
with:
|
||||||
project-url: https://github.com/orgs/element-hq/projects/41
|
project-url: https://github.com/orgs/element-hq/projects/41
|
||||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
@@ -156,7 +156,7 @@ jobs:
|
|||||||
if: >
|
if: >
|
||||||
contains(github.event.issue.labels.*.name, 'Team: Crypto')
|
contains(github.event.issue.labels.*.name, 'Team: Crypto')
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/add-to-project@main
|
- uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2
|
||||||
with:
|
with:
|
||||||
project-url: https://github.com/orgs/element-hq/projects/76
|
project-url: https://github.com/orgs/element-hq/projects/76
|
||||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
@@ -172,7 +172,7 @@ jobs:
|
|||||||
contains(github.event.issue.labels.*.name, 'A-Testing') ||
|
contains(github.event.issue.labels.*.name, 'A-Testing') ||
|
||||||
contains(github.event.issue.labels.*.name, 'Z-Flaky-Test')
|
contains(github.event.issue.labels.*.name, 'Z-Flaky-Test')
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/add-to-project@main
|
- uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2
|
||||||
with:
|
with:
|
||||||
project-url: https://github.com/orgs/element-hq/projects/101
|
project-url: https://github.com/orgs/element-hq/projects/101
|
||||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ jobs:
|
|||||||
name: Move PRs asking for design review to the design board
|
name: Move PRs asking for design review to the design board
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: octokit/graphql-action@v2.x
|
- uses: octokit/graphql-action@8ad880e4d437783ea2ab17010324de1075228110 # v2.3.2
|
||||||
id: find_team_members
|
id: find_team_members
|
||||||
with:
|
with:
|
||||||
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
||||||
@@ -52,7 +52,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
env:
|
env:
|
||||||
TEAM: "design"
|
TEAM: "design"
|
||||||
- uses: octokit/graphql-action@v2.x
|
- uses: octokit/graphql-action@8ad880e4d437783ea2ab17010324de1075228110 # v2.3.2
|
||||||
id: add_to_project
|
id: add_to_project
|
||||||
if: steps.any_matching_reviewers.outputs.match == 'true'
|
if: steps.any_matching_reviewers.outputs.match == 'true'
|
||||||
with:
|
with:
|
||||||
@@ -76,7 +76,7 @@ jobs:
|
|||||||
name: Move PRs asking for design review to the design board
|
name: Move PRs asking for design review to the design board
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: octokit/graphql-action@v2.x
|
- uses: octokit/graphql-action@8ad880e4d437783ea2ab17010324de1075228110 # v2.3.2
|
||||||
id: find_team_members
|
id: find_team_members
|
||||||
with:
|
with:
|
||||||
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
||||||
@@ -119,7 +119,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
env:
|
env:
|
||||||
TEAM: "product"
|
TEAM: "product"
|
||||||
- uses: octokit/graphql-action@v2.x
|
- uses: octokit/graphql-action@8ad880e4d437783ea2ab17010324de1075228110 # v2.3.2
|
||||||
id: add_to_project
|
id: add_to_project
|
||||||
if: steps.any_matching_reviewers.outputs.match == 'true'
|
if: steps.any_matching_reviewers.outputs.match == 'true'
|
||||||
with:
|
with:
|
||||||
|
|||||||
2
.github/workflows/update-jitsi.yml
vendored
@@ -9,7 +9,7 @@ jobs:
|
|||||||
update:
|
update:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||||
|
|
||||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
82
CHANGELOG.md
@@ -1,3 +1,85 @@
|
|||||||
|
Changes in [1.11.111](https://github.com/element-hq/element-web/releases/tag/v1.11.111) (2025-09-10)
|
||||||
|
====================================================================================================
|
||||||
|
## ✨ Features
|
||||||
|
|
||||||
|
* Do not hide media from your own user by default ([#29797](https://github.com/element-hq/element-web/pull/29797)). Contributed by @Half-Shot.
|
||||||
|
* Remember whether sidebar is shown for calls when switching rooms ([#30262](https://github.com/element-hq/element-web/pull/30262)). Contributed by @bojidar-bg.
|
||||||
|
* Open the proper integration settings on integrations disabled error ([#30538](https://github.com/element-hq/element-web/pull/30538)). Contributed by @Half-Shot.
|
||||||
|
* Show a "progress" dialog while invites are being sent ([#30561](https://github.com/element-hq/element-web/pull/30561)). Contributed by @richvdh.
|
||||||
|
* Move the room list to the new ListView(backed by react-virtuoso) ([#30515](https://github.com/element-hq/element-web/pull/30515)). Contributed by @langleyd.
|
||||||
|
|
||||||
|
## 🐛 Bug Fixes
|
||||||
|
|
||||||
|
* [Backport staging] Ensure container starts if it is mounted with an empty /modules directory. ([#30705](https://github.com/element-hq/element-web/pull/30705)). Contributed by @RiotRobot.
|
||||||
|
* Fix room joining over federation not specifying vias or using aliases ([#30641](https://github.com/element-hq/element-web/pull/30641)). Contributed by @t3chguy.
|
||||||
|
* Fix stable-suffixed MSC4133 support ([#30649](https://github.com/element-hq/element-web/pull/30649)). Contributed by @dbkr.
|
||||||
|
* Fix i18n of message when a setting is disabled ([#30646](https://github.com/element-hq/element-web/pull/30646)). Contributed by @dbkr.
|
||||||
|
* ListView should not handle the arrow keys if there is a modifier applied ([#30633](https://github.com/element-hq/element-web/pull/30633)). Contributed by @langleyd.
|
||||||
|
* Make BaseDialog's div keyboard focusable and fix test. ([#30631](https://github.com/element-hq/element-web/pull/30631)). Contributed by @langleyd.
|
||||||
|
* Fix: Allow triple-click text selection to flow around pills ([#30349](https://github.com/element-hq/element-web/pull/30349)). Contributed by @AlirezaMrtz.
|
||||||
|
* Watch for a 'join' action to know when the call is connected ([#29492](https://github.com/element-hq/element-web/pull/29492)). Contributed by @robintown.
|
||||||
|
* Fix: add missing tooltip and aria-label to lock icon next to composer ([#30623](https://github.com/element-hq/element-web/pull/30623)). Contributed by @florianduros.
|
||||||
|
* Don't render context menu when scrolling ([#30613](https://github.com/element-hq/element-web/pull/30613)). Contributed by @langleyd.
|
||||||
|
|
||||||
|
|
||||||
|
Changes in [1.11.110](https://github.com/element-hq/element-web/releases/tag/v1.11.110) (2025-08-27)
|
||||||
|
====================================================================================================
|
||||||
|
## ✨ Features
|
||||||
|
|
||||||
|
* Hide recovery key when re-entering it while creating or changing it ([#30499](https://github.com/element-hq/element-web/pull/30499)). Contributed by @andybalaam.
|
||||||
|
* Add `?no_universal_links=true` to OIDC url so EX doesn't try to handle it ([#29439](https://github.com/element-hq/element-web/pull/29439)). Contributed by @t3chguy.
|
||||||
|
* Show a blue lock for unencrypted rooms and hide the grey shield for encrypted rooms ([#30440](https://github.com/element-hq/element-web/pull/30440)). Contributed by @langleyd.
|
||||||
|
* Add support for Module API 1.4 ([#30185](https://github.com/element-hq/element-web/pull/30185)). Contributed by @t3chguy.
|
||||||
|
* MVVM - Introduce some helpers for snapshot management ([#30398](https://github.com/element-hq/element-web/pull/30398)). Contributed by @MidhunSureshR.
|
||||||
|
|
||||||
|
## 🐛 Bug Fixes
|
||||||
|
|
||||||
|
* A11y: move focus to right panel when opened ([#30553](https://github.com/element-hq/element-web/pull/30553)). Contributed by @florianduros.
|
||||||
|
* Fix e2e warning icon should be white ([#30539](https://github.com/element-hq/element-web/pull/30539)). Contributed by @florianduros.
|
||||||
|
* Remove NoOneHere disabled reason. ([#30524](https://github.com/element-hq/element-web/pull/30524)). Contributed by @toger5.
|
||||||
|
* Fix downloading files with authenticated media API ([#30520](https://github.com/element-hq/element-web/pull/30520)). Contributed by @t3chguy.
|
||||||
|
* Fix call permissions check confusion around element call ([#30521](https://github.com/element-hq/element-web/pull/30521)). Contributed by @t3chguy.
|
||||||
|
* Fix line wrap around emoji verification ([#30523](https://github.com/element-hq/element-web/pull/30523)). Contributed by @t3chguy.
|
||||||
|
* Don't highlight redacted events ([#30519](https://github.com/element-hq/element-web/pull/30519)). Contributed by @t3chguy.
|
||||||
|
* Fix matrix.to links not being handled in the app ([#30522](https://github.com/element-hq/element-web/pull/30522)). Contributed by @t3chguy.
|
||||||
|
* Fix issue of new room list taking up the full width ([#30459](https://github.com/element-hq/element-web/pull/30459)). Contributed by @langleyd.
|
||||||
|
* Fix widget persistence in React development mode ([#30509](https://github.com/element-hq/element-web/pull/30509)). Contributed by @robintown.
|
||||||
|
* Fix widget initialization in React development mode ([#30463](https://github.com/element-hq/element-web/pull/30463)). Contributed by @robintown.
|
||||||
|
|
||||||
|
|
||||||
|
Changes in [1.11.109](https://github.com/element-hq/element-web/releases/tag/v1.11.109) (2025-08-11)
|
||||||
|
====================================================================================================
|
||||||
|
This release supports the upcoming v12 ("hydra") Matrix room version and is necessary to view and participate in these rooms.
|
||||||
|
|
||||||
|
## ✨ Features
|
||||||
|
|
||||||
|
* [Backport staging] Allow /upgraderoom command without developer mode enabled ([#30529](https://github.com/element-hq/element-web/pull/30529)). Contributed by @RiotRobot.
|
||||||
|
* [Backport staging] Support for creator/owner power level ([#30526](https://github.com/element-hq/element-web/pull/30526)). Contributed by @RiotRobot.
|
||||||
|
* New room list: change icon and label of menu item for to start a DM ([#30470](https://github.com/element-hq/element-web/pull/30470)). Contributed by @florianduros.
|
||||||
|
* Implement the member list with virtuoso ([#29869](https://github.com/element-hq/element-web/pull/29869)). Contributed by @langleyd.
|
||||||
|
* Add labs option for history sharing on invite ([#30313](https://github.com/element-hq/element-web/pull/30313)). Contributed by @richvdh.
|
||||||
|
* Bump wysiwyg to 2.39.0 adding support for pasting rich text content in the Rich Text Edtior ([#30421](https://github.com/element-hq/element-web/pull/30421)). Contributed by @langleyd.
|
||||||
|
* Support `EventShieldReason.MISMATCHED_SENDER` ([#30403](https://github.com/element-hq/element-web/pull/30403)). Contributed by @richvdh.
|
||||||
|
* Change unencrypted and public pills to blue ([#30399](https://github.com/element-hq/element-web/pull/30399)). Contributed by @florianduros.
|
||||||
|
* Change color of public room icon ([#30390](https://github.com/element-hq/element-web/pull/30390)). Contributed by @florianduros.
|
||||||
|
* Script for updating storybook screenshots ([#30340](https://github.com/element-hq/element-web/pull/30340)). Contributed by @dbkr.
|
||||||
|
* Add toggle to hide empty state in devtools ([#30352](https://github.com/element-hq/element-web/pull/30352)). Contributed by @toger5.
|
||||||
|
|
||||||
|
## 🐛 Bug Fixes
|
||||||
|
|
||||||
|
* [Backport staging] Use userId to filter users in non-federated rooms when showing the InviteDialog ([#30537](https://github.com/element-hq/element-web/pull/30537)). Contributed by @RiotRobot.
|
||||||
|
* [Backport staging] Catch error when encountering invalid m.room.pinned\_events event ([#30536](https://github.com/element-hq/element-web/pull/30536)). Contributed by @RiotRobot.
|
||||||
|
* Update for compatibility with v12 rooms ([#30452](https://github.com/element-hq/element-web/pull/30452)). Contributed by @dbkr.
|
||||||
|
* New room list: fix tooltip on presence ([#30474](https://github.com/element-hq/element-web/pull/30474)). Contributed by @florianduros.
|
||||||
|
* New room list: add tooltip for presence and room status ([#30472](https://github.com/element-hq/element-web/pull/30472)). Contributed by @florianduros.
|
||||||
|
* Fix: Clicking on an item in the member list causes it to scroll to the top rather than show the profile view ([#30455](https://github.com/element-hq/element-web/pull/30455)). Contributed by @langleyd.
|
||||||
|
* Put the 'decrypting' tooltip back ([#30446](https://github.com/element-hq/element-web/pull/30446)). Contributed by @dbkr.
|
||||||
|
* Use server name explicitly for via. ([#30362](https://github.com/element-hq/element-web/pull/30362)). Contributed by @Half-Shot.
|
||||||
|
* fix: replace hardcoded string in poll history dialog ([#30402](https://github.com/element-hq/element-web/pull/30402)). Contributed by @florianduros.
|
||||||
|
* fix: replace hardcoded string on qr code back button ([#30401](https://github.com/element-hq/element-web/pull/30401)). Contributed by @florianduros.
|
||||||
|
* Fix color of icon button with outline ([#30361](https://github.com/element-hq/element-web/pull/30361)). Contributed by @florianduros.
|
||||||
|
|
||||||
|
|
||||||
Changes in [1.11.108](https://github.com/element-hq/element-web/releases/tag/v1.11.108) (2025-07-30)
|
Changes in [1.11.108](https://github.com/element-hq/element-web/releases/tag/v1.11.108) (2025-07-30)
|
||||||
====================================================================================================
|
====================================================================================================
|
||||||
## 🐛 Bug Fixes
|
## 🐛 Bug Fixes
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# syntax=docker.io/docker/dockerfile:1.17-labs@sha256:9187104f31e3a002a8a6a3209ea1f937fb7486c093cbbde1e14b0fa0d7e4f1b5
|
# syntax=docker.io/docker/dockerfile:1.17-labs@sha256:9187104f31e3a002a8a6a3209ea1f937fb7486c093cbbde1e14b0fa0d7e4f1b5
|
||||||
|
|
||||||
# Builder
|
# Builder
|
||||||
FROM --platform=$BUILDPLATFORM node:22-bullseye@sha256:2d63e0f812d023c4c764e83d7e30dc94949304443ebc372d5c445e63a5ae49c1 AS builder
|
FROM --platform=$BUILDPLATFORM node:22-bullseye@sha256:9e34ba52e1f3c31ed9bd4d0bcf784f5909db17cda61c220e29c8d7a8ebfb402e 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
|
||||||
@@ -19,7 +19,7 @@ RUN /src/scripts/docker-package.sh
|
|||||||
RUN cp /src/config.sample.json /src/webapp/config.json
|
RUN cp /src/config.sample.json /src/webapp/config.json
|
||||||
|
|
||||||
# App
|
# App
|
||||||
FROM nginxinc/nginx-unprivileged:alpine-slim@sha256:e61b77b27c8f3124fad6d19e894ca5b603bcaf6a34a2df035511299dfa6fad35
|
FROM nginxinc/nginx-unprivileged:alpine-slim@sha256:ea6c4b8b568824ea94cd1fabd47e1c4e7c0c04744f344a3793f7e9c8ac3a3636
|
||||||
|
|
||||||
# Need root user to install packages & manipulate the usr directory
|
# Need root user to install packages & manipulate the usr directory
|
||||||
USER root
|
USER root
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ Element has several tiers of support for different environments:
|
|||||||
- Best effort
|
- Best effort
|
||||||
- Definition:
|
- Definition:
|
||||||
- Issues **accepted**, regressions **do not block** the release
|
- Issues **accepted**, regressions **do not block** the release
|
||||||
- The wider Element Products(including Element Call and the Enterprise Server Suite) do still not officially support these browsers.
|
- The wider Element Products (including Element Call and the Enterprise Server Suite) do still not officially support these browsers.
|
||||||
- The element web project and its contributors should keep the client functioning and gracefully degrade where other sibling features (E.g. Element Call) may not function.
|
- The element web project and its contributors should keep the client functioning and gracefully degrade where other sibling features (E.g. Element Call) may not function.
|
||||||
- Last major release of Firefox ESR and Chrome/Edge Extended Stable
|
- Last major release of Firefox ESR and Chrome/Edge Extended Stable
|
||||||
- Community Supported
|
- Community Supported
|
||||||
|
|||||||
@@ -14,10 +14,9 @@ entrypoint_log() {
|
|||||||
mkdir -p /tmp/element-web-config
|
mkdir -p /tmp/element-web-config
|
||||||
cp /app/config*.json /tmp/element-web-config/
|
cp /app/config*.json /tmp/element-web-config/
|
||||||
|
|
||||||
# If there are modules to be loaded
|
# If the module directory exists AND the module directory has modules in it
|
||||||
if [ -d "/modules" ]; then
|
if [ -d "/modules" ] && [ "$( ls -A '/modules' )" ]; then
|
||||||
cd /modules
|
cd /modules
|
||||||
|
|
||||||
for MODULE in *
|
for MODULE in *
|
||||||
do
|
do
|
||||||
# If the module has a package.json, use its main field as the entrypoint
|
# If the module has a package.json, use its main field as the entrypoint
|
||||||
|
|||||||
7
knip.ts
@@ -42,6 +42,13 @@ export default {
|
|||||||
"util",
|
"util",
|
||||||
// Embedded into webapp
|
// Embedded into webapp
|
||||||
"@element-hq/element-call-embedded",
|
"@element-hq/element-call-embedded",
|
||||||
|
|
||||||
|
// Used by matrix-js-sdk, which means we have to include them as a
|
||||||
|
// dependency so that // we can run `tsc` (since we import the typescript
|
||||||
|
// source of js-sdk, rather than the transpiled and annotated JS like you
|
||||||
|
// would with a normal library).
|
||||||
|
"@types/content-type",
|
||||||
|
"@types/sdp-transform",
|
||||||
],
|
],
|
||||||
ignoreBinaries: [
|
ignoreBinaries: [
|
||||||
// Used in scripts & workflows
|
// Used in scripts & workflows
|
||||||
|
|||||||
18
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "element-web",
|
"name": "element-web",
|
||||||
"version": "1.11.108",
|
"version": "1.11.111",
|
||||||
"description": "Element: the future of secure communication",
|
"description": "Element: the future of secure communication",
|
||||||
"author": "New Vector Ltd.",
|
"author": "New Vector Ltd.",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -75,7 +75,7 @@
|
|||||||
"resolutions": {
|
"resolutions": {
|
||||||
"**/pretty-format/react-is": "19.1.1",
|
"**/pretty-format/react-is": "19.1.1",
|
||||||
"@playwright/test": "1.54.2",
|
"@playwright/test": "1.54.2",
|
||||||
"@types/react": "19.1.9",
|
"@types/react": "19.1.10",
|
||||||
"@types/react-dom": "19.1.7",
|
"@types/react-dom": "19.1.7",
|
||||||
"oidc-client-ts": "3.3.0",
|
"oidc-client-ts": "3.3.0",
|
||||||
"jwt-decode": "4.0.0",
|
"jwt-decode": "4.0.0",
|
||||||
@@ -96,7 +96,6 @@
|
|||||||
"@matrix-org/spec": "^1.7.0",
|
"@matrix-org/spec": "^1.7.0",
|
||||||
"@sentry/browser": "^10.0.0",
|
"@sentry/browser": "^10.0.0",
|
||||||
"@types/png-chunks-extract": "^1.0.2",
|
"@types/png-chunks-extract": "^1.0.2",
|
||||||
"@types/react-virtualized": "^9.21.30",
|
|
||||||
"@vector-im/compound-design-tokens": "^6.0.0",
|
"@vector-im/compound-design-tokens": "^6.0.0",
|
||||||
"@vector-im/compound-web": "^8.1.2",
|
"@vector-im/compound-web": "^8.1.2",
|
||||||
"@vector-im/matrix-wysiwyg": "2.39.0",
|
"@vector-im/matrix-wysiwyg": "2.39.0",
|
||||||
@@ -135,7 +134,7 @@
|
|||||||
"maplibre-gl": "^5.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": "38.1.0",
|
||||||
"matrix-widget-api": "^1.10.0",
|
"matrix-widget-api": "^1.10.0",
|
||||||
"memoize-one": "^6.0.0",
|
"memoize-one": "^6.0.0",
|
||||||
"mime": "^4.0.4",
|
"mime": "^4.0.4",
|
||||||
@@ -143,7 +142,7 @@
|
|||||||
"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.257.0",
|
"posthog-js": "1.260.1",
|
||||||
"qrcode": "1.5.4",
|
"qrcode": "1.5.4",
|
||||||
"re-resizable": "6.11.2",
|
"re-resizable": "6.11.2",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
@@ -153,8 +152,7 @@
|
|||||||
"react-focus-lock": "^2.5.1",
|
"react-focus-lock": "^2.5.1",
|
||||||
"react-string-replace": "^1.1.1",
|
"react-string-replace": "^1.1.1",
|
||||||
"react-transition-group": "^4.4.1",
|
"react-transition-group": "^4.4.1",
|
||||||
"react-virtualized": "^9.22.5",
|
"react-virtuoso": "^4.14.0",
|
||||||
"react-virtuoso": "^4.12.6",
|
|
||||||
"rfc4648": "^1.4.0",
|
"rfc4648": "^1.4.0",
|
||||||
"sanitize-filename": "^1.6.3",
|
"sanitize-filename": "^1.6.3",
|
||||||
"sanitize-html": "2.17.0",
|
"sanitize-html": "2.17.0",
|
||||||
@@ -187,7 +185,7 @@
|
|||||||
"@babel/runtime": "^7.12.5",
|
"@babel/runtime": "^7.12.5",
|
||||||
"@casualbot/jest-sonar-reporter": "2.2.7",
|
"@casualbot/jest-sonar-reporter": "2.2.7",
|
||||||
"@element-hq/element-call-embedded": "0.14.1",
|
"@element-hq/element-call-embedded": "0.14.1",
|
||||||
"@element-hq/element-web-playwright-common": "^1.4.4",
|
"@element-hq/element-web-playwright-common": "^1.4.6",
|
||||||
"@peculiar/webcrypto": "^1.4.3",
|
"@peculiar/webcrypto": "^1.4.3",
|
||||||
"@playwright/test": "^1.50.1",
|
"@playwright/test": "^1.50.1",
|
||||||
"@principalstudio/html-webpack-inject-preload": "^1.2.7",
|
"@principalstudio/html-webpack-inject-preload": "^1.2.7",
|
||||||
@@ -205,6 +203,7 @@
|
|||||||
"@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.9",
|
||||||
"@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",
|
||||||
@@ -223,11 +222,12 @@
|
|||||||
"@types/node-fetch": "^2.6.2",
|
"@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": "19.1.9",
|
"@types/react": "19.1.10",
|
||||||
"@types/react-beautiful-dnd": "^13.0.0",
|
"@types/react-beautiful-dnd": "^13.0.0",
|
||||||
"@types/react-dom": "19.1.7",
|
"@types/react-dom": "19.1.7",
|
||||||
"@types/react-transition-group": "^4.4.0",
|
"@types/react-transition-group": "^4.4.0",
|
||||||
"@types/sanitize-html": "2.16.0",
|
"@types/sanitize-html": "2.16.0",
|
||||||
|
"@types/sdp-transform": "^2.4.10",
|
||||||
"@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",
|
||||||
|
|||||||
@@ -158,7 +158,8 @@ test.describe("Cryptography", function () {
|
|||||||
await page.getByRole("textbox", { name: "Send an unencrypted message…" }).press("Enter");
|
await page.getByRole("textbox", { name: "Send an unencrypted message…" }).press("Enter");
|
||||||
await checkDMRoom(page);
|
await checkDMRoom(page);
|
||||||
const bobRoomId = await bobJoin(page, bob);
|
const bobRoomId = await bobJoin(page, bob);
|
||||||
await expect(page.locator(".mx_MessageComposer_e2eIcon")).toMatchScreenshot("composer-e2e-icon-normal.png");
|
// We no longer show the grey badge in the composer, check that it is not there.
|
||||||
|
await expect(page.locator(".mx_MessageComposer_e2eIcon")).toHaveCount(0);
|
||||||
|
|
||||||
await testMessages(page, bob, bobRoomId);
|
await testMessages(page, bob, bobRoomId);
|
||||||
await verify(app, bob);
|
await verify(app, bob);
|
||||||
|
|||||||
@@ -58,108 +58,108 @@ test.describe("Cryptography", function () {
|
|||||||
await app.client.network.setupRoute();
|
await app.client.network.setupRoute();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should show the correct shield on e2e events", async ({
|
test(
|
||||||
page,
|
"should show the correct shield on e2e events",
|
||||||
app,
|
{ tag: "@screenshot" },
|
||||||
bot: bob,
|
async ({ page, app, bot: bob, homeserver }, workerInfo) => {
|
||||||
homeserver,
|
// Bob has a second, not cross-signed, device
|
||||||
}, workerInfo) => {
|
const bobSecondDevice = await createSecondBotDevice(page, homeserver, bob);
|
||||||
// Bob has a second, not cross-signed, device
|
|
||||||
const bobSecondDevice = await createSecondBotDevice(page, homeserver, bob);
|
|
||||||
|
|
||||||
// Dismiss the toasts nagging us, otherwise they get in the way of clicking the room list
|
// Dismiss the toasts nagging us, otherwise they get in the way of clicking the room list
|
||||||
await page.getByRole("button", { name: "Dismiss" }).click();
|
await page.getByRole("button", { name: "Dismiss" }).click();
|
||||||
await page.getByRole("button", { name: "Yes, dismiss" }).click();
|
await page.getByRole("button", { name: "Yes, dismiss" }).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",
|
||||||
});
|
});
|
||||||
|
|
||||||
const last = page.locator(".mx_EventTile_last");
|
const last = page.locator(".mx_EventTile_last");
|
||||||
await expect(last).toContainText("Unable to decrypt message");
|
await expect(last).toContainText("Unable to decrypt message");
|
||||||
const lastE2eIcon = last.locator(".mx_EventTile_e2eIcon");
|
const lastE2eIcon = last.locator(".mx_EventTile_e2eIcon");
|
||||||
await expect(lastE2eIcon).toHaveClass(/mx_EventTile_e2eIcon_decryption_failure/);
|
await expect(lastE2eIcon).toHaveClass(/mx_EventTile_e2eIcon_decryption_failure/);
|
||||||
await lastE2eIcon.focus();
|
await lastE2eIcon.focus();
|
||||||
await expect(await app.getTooltipForElement(lastE2eIcon)).toContainText(
|
await expect(await app.getTooltipForElement(lastE2eIcon)).toContainText(
|
||||||
"This message could not be decrypted",
|
"This message could not be decrypted",
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Should show a red padlock for an unencrypted message in an e2e room */
|
/* Should show a red padlock for an unencrypted message in an e2e room */
|
||||||
await bob.evaluate(
|
await bob.evaluate(
|
||||||
(cli, testRoomId) =>
|
(cli, testRoomId) =>
|
||||||
cli.http.authedRequest(
|
cli.http.authedRequest(
|
||||||
window.matrixcs.Method.Put,
|
window.matrixcs.Method.Put,
|
||||||
`/rooms/${encodeURIComponent(testRoomId)}/send/m.room.message/test_txn_1`,
|
`/rooms/${encodeURIComponent(testRoomId)}/send/m.room.message/test_txn_1`,
|
||||||
undefined,
|
undefined,
|
||||||
{
|
{
|
||||||
msgtype: "m.text",
|
msgtype: "m.text",
|
||||||
body: "test unencrypted",
|
body: "test unencrypted",
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
testRoomId,
|
testRoomId,
|
||||||
);
|
);
|
||||||
|
|
||||||
await expect(last).toContainText("test unencrypted");
|
await expect(last).toContainText("test unencrypted");
|
||||||
await expect(lastE2eIcon).toHaveClass(/mx_EventTile_e2eIcon_warning/);
|
await expect(lastE2eIcon).toHaveClass(/mx_EventTile_e2eIcon_warning/);
|
||||||
await lastE2eIcon.focus();
|
await expect(lastE2eIcon).toMatchScreenshot("event-shield-warning.png");
|
||||||
await expect(await app.getTooltipForElement(lastE2eIcon)).toContainText("Not encrypted");
|
await lastE2eIcon.focus();
|
||||||
|
await expect(await app.getTooltipForElement(lastE2eIcon)).toContainText("Not encrypted");
|
||||||
|
|
||||||
/* Should show no padlock for an unverified user */
|
/* Should show no padlock for an unverified user */
|
||||||
// bob sends a valid event
|
// bob sends a valid event
|
||||||
await bob.sendMessage(testRoomId, "test encrypted 1");
|
await bob.sendMessage(testRoomId, "test encrypted 1");
|
||||||
|
|
||||||
// the message should appear, decrypted, with no warning, but also no "verified"
|
// the message should appear, decrypted, with no warning, but also no "verified"
|
||||||
const lastTile = page.locator(".mx_EventTile_last");
|
const lastTile = page.locator(".mx_EventTile_last");
|
||||||
const lastTileE2eIcon = lastTile.locator(".mx_EventTile_e2eIcon");
|
const lastTileE2eIcon = lastTile.locator(".mx_EventTile_e2eIcon");
|
||||||
await expect(lastTile).toContainText("test encrypted 1");
|
await expect(lastTile).toContainText("test encrypted 1");
|
||||||
// no e2e icon
|
// no e2e icon
|
||||||
await expect(lastTileE2eIcon).not.toBeVisible();
|
await expect(lastTileE2eIcon).not.toBeVisible();
|
||||||
|
|
||||||
/* Now verify Bob */
|
/* Now verify Bob */
|
||||||
await verify(app, bob);
|
await verify(app, bob);
|
||||||
|
|
||||||
/* Existing message should be updated when user is verified. */
|
/* Existing message should be updated when user is verified. */
|
||||||
await expect(last).toContainText("test encrypted 1");
|
await expect(last).toContainText("test encrypted 1");
|
||||||
// still no e2e icon
|
// still no e2e icon
|
||||||
await expect(last.locator(".mx_EventTile_e2eIcon")).not.toBeVisible();
|
await expect(last.locator(".mx_EventTile_e2eIcon")).not.toBeVisible();
|
||||||
|
|
||||||
/* should show no padlock, and be verified, for a message from a verified device */
|
/* should show no padlock, and be verified, for a message from a verified device */
|
||||||
await bob.sendMessage(testRoomId, "test encrypted 2");
|
await bob.sendMessage(testRoomId, "test encrypted 2");
|
||||||
|
|
||||||
await expect(lastTile).toContainText("test encrypted 2");
|
await expect(lastTile).toContainText("test encrypted 2");
|
||||||
// no e2e icon
|
// no e2e icon
|
||||||
await expect(lastTileE2eIcon).not.toBeVisible();
|
await expect(lastTileE2eIcon).not.toBeVisible();
|
||||||
|
|
||||||
/* should show red padlock for a message from an unverified device */
|
/* should show red padlock for a message from an unverified device */
|
||||||
await bobSecondDevice.sendMessage(testRoomId, "test encrypted from unverified");
|
await bobSecondDevice.sendMessage(testRoomId, "test encrypted from unverified");
|
||||||
await expect(lastTile).toContainText("test encrypted from unverified");
|
await expect(lastTile).toContainText("test encrypted from unverified");
|
||||||
await expect(lastTileE2eIcon).toHaveClass(/mx_EventTile_e2eIcon_warning/);
|
await expect(lastTileE2eIcon).toHaveClass(/mx_EventTile_e2eIcon_warning/);
|
||||||
await lastTileE2eIcon.focus();
|
await lastTileE2eIcon.focus();
|
||||||
await expect(await app.getTooltipForElement(lastTileE2eIcon)).toContainText(
|
await expect(await app.getTooltipForElement(lastTileE2eIcon)).toContainText(
|
||||||
"Encrypted by a device not verified by its owner.",
|
"Encrypted by a device not verified by its owner.",
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Should show a red padlock for a message from an unverified device.
|
/* 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
|
||||||
await bobSecondDevice.evaluate((cli) => cli.logout(true));
|
await bobSecondDevice.evaluate((cli) => cli.logout(true));
|
||||||
|
|
||||||
// wait for the logout to propagate.
|
// wait for the logout to propagate.
|
||||||
await waitForDevices(app, bob.credentials.userId, 1);
|
await waitForDevices(app, bob.credentials.userId, 1);
|
||||||
|
|
||||||
// close and reopen the room, to get the shield to update.
|
// close and reopen the room, to get the shield to update.
|
||||||
await app.viewRoomByName("Bob");
|
await app.viewRoomByName("Bob");
|
||||||
await app.viewRoomByName("TestRoom");
|
await app.viewRoomByName("TestRoom");
|
||||||
|
|
||||||
await expect(last).toContainText("test encrypted from unverified");
|
await expect(last).toContainText("test encrypted from unverified");
|
||||||
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(
|
||||||
"Encrypted by a device not verified by its owner.",
|
"Encrypted by a device not verified by its owner.",
|
||||||
);
|
);
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
test("Should show a grey padlock for a key restored from backup", async ({
|
test("Should show a grey padlock for a key restored from backup", async ({
|
||||||
page,
|
page,
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ test.describe("'Turn on key storage' toast", () => {
|
|||||||
await toast.getByRole("button", { name: "Continue" }).click();
|
await toast.getByRole("button", { name: "Continue" }).click();
|
||||||
|
|
||||||
// Then we see the Encryption settings dialog with an option to turn on key storage
|
// Then we see the Encryption settings dialog with an option to turn on key storage
|
||||||
await expect(page.getByRole("checkbox", { name: "Allow key storage" })).toBeVisible();
|
await expect(page.getByRole("switch", { name: "Allow key storage" })).toBeVisible();
|
||||||
|
|
||||||
// And when we close that
|
// And when we close that
|
||||||
await page.getByRole("button", { name: "Close dialog" }).click();
|
await page.getByRole("button", { name: "Close dialog" }).click();
|
||||||
@@ -153,7 +153,7 @@ test.describe("'Turn on key storage' toast", () => {
|
|||||||
await page.getByRole("button", { name: "Go to Settings" }).click();
|
await page.getByRole("button", { name: "Go to Settings" }).click();
|
||||||
|
|
||||||
// Then we see Encryption settings again
|
// Then we see Encryption settings again
|
||||||
await expect(page.getByRole("checkbox", { name: "Allow key storage" })).toBeVisible();
|
await expect(page.getByRole("switch", { name: "Allow key storage" })).toBeVisible();
|
||||||
|
|
||||||
// And when we close that, see the toast, click Dismiss, and Yes, Dismiss
|
// And when we close that, see the toast, click Dismiss, and Yes, Dismiss
|
||||||
await page.getByRole("button", { name: "Close dialog" }).click();
|
await page.getByRole("button", { name: "Close dialog" }).click();
|
||||||
|
|||||||
@@ -300,9 +300,9 @@ export async function doTwoWaySasVerification(page: Page, verifier: JSHandle<Ver
|
|||||||
export async function enableKeyBackup(app: ElementAppPage): Promise<string> {
|
export async function enableKeyBackup(app: ElementAppPage): Promise<string> {
|
||||||
const encryptionTab = await app.settings.openUserSettings("Encryption");
|
const encryptionTab = await app.settings.openUserSettings("Encryption");
|
||||||
|
|
||||||
const keyStorageToggle = encryptionTab.getByRole("checkbox", { name: "Allow key storage" });
|
const keyStorageToggle = encryptionTab.getByRole("switch", { name: "Allow key storage" });
|
||||||
if (!(await keyStorageToggle.isChecked())) {
|
if (!(await keyStorageToggle.isChecked())) {
|
||||||
await encryptionTab.getByRole("checkbox", { name: "Allow key storage" }).click();
|
await encryptionTab.getByRole("switch", { name: "Allow key storage" }).click();
|
||||||
}
|
}
|
||||||
|
|
||||||
await encryptionTab.getByRole("button", { name: "Set up recovery" }).click();
|
await encryptionTab.getByRole("button", { name: "Set up recovery" }).click();
|
||||||
@@ -323,11 +323,11 @@ export async function enableKeyBackup(app: ElementAppPage): Promise<string> {
|
|||||||
export async function disableKeyBackup(app: ElementAppPage): Promise<void> {
|
export async function disableKeyBackup(app: ElementAppPage): Promise<void> {
|
||||||
const encryptionTab = await app.settings.openUserSettings("Encryption");
|
const encryptionTab = await app.settings.openUserSettings("Encryption");
|
||||||
|
|
||||||
const keyStorageToggle = encryptionTab.getByRole("checkbox", { name: "Allow key storage" });
|
const keyStorageToggle = encryptionTab.getByRole("switch", { name: "Allow key storage" });
|
||||||
if (await keyStorageToggle.isChecked()) {
|
if (await keyStorageToggle.isChecked()) {
|
||||||
await encryptionTab.getByRole("checkbox", { name: "Allow key storage" }).click();
|
await encryptionTab.getByRole("switch", { name: "Allow key storage" }).click();
|
||||||
await encryptionTab.getByRole("button", { name: "Delete key storage" }).click();
|
await encryptionTab.getByRole("button", { name: "Delete key storage" }).click();
|
||||||
await encryptionTab.getByRole("checkbox", { name: "Allow key storage" }).isVisible();
|
await encryptionTab.getByRole("switch", { name: "Allow key storage" }).isVisible();
|
||||||
|
|
||||||
// Wait for the update to account data to stick
|
// Wait for the update to account data to stick
|
||||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ test.describe("Room list filters and sort", () => {
|
|||||||
So we expect 'Old Room' to show up in the room list.
|
So we expect 'Old Room' to show up in the room list.
|
||||||
*/
|
*/
|
||||||
const roomListView = getRoomList(page);
|
const roomListView = getRoomList(page);
|
||||||
const oldRoomTile = roomListView.getByRole("gridcell", { name: "Open room Old Room" });
|
const oldRoomTile = roomListView.getByRole("option", { name: "Open room Old Room" });
|
||||||
await expect(oldRoomTile).toBeVisible();
|
await expect(oldRoomTile).toBeVisible();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -139,8 +139,9 @@ test.describe("Room list filters and sort", () => {
|
|||||||
|
|
||||||
// Open the non-favourite room
|
// Open the non-favourite room
|
||||||
const roomListView = getRoomList(page);
|
const roomListView = getRoomList(page);
|
||||||
const tile = roomListView.getByRole("gridcell", { name: "Open room room-non-fav" });
|
const tile = roomListView.getByRole("option", { name: "Open room room-non-fav" });
|
||||||
await tile.scrollIntoViewIfNeeded();
|
// item may not be in the DOM using scrollListToBottom rather than scrollIntoViewIfNeeded
|
||||||
|
await app.scrollListToBottom(roomListView);
|
||||||
await tile.click();
|
await tile.click();
|
||||||
|
|
||||||
// Enable Favourite filter
|
// Enable Favourite filter
|
||||||
@@ -151,7 +152,7 @@ test.describe("Room list filters and sort", () => {
|
|||||||
|
|
||||||
// Ensure the room list is not scrolled
|
// Ensure the room list is not scrolled
|
||||||
const isScrolledDown = await page
|
const isScrolledDown = await page
|
||||||
.getByRole("grid", { name: "Room list" })
|
.getByRole("listbox", { name: "Room list", exact: true })
|
||||||
.evaluate((e) => e.scrollTop !== 0);
|
.evaluate((e) => e.scrollTop !== 0);
|
||||||
expect(isScrolledDown).toStrictEqual(false);
|
expect(isScrolledDown).toStrictEqual(false);
|
||||||
});
|
});
|
||||||
@@ -227,37 +228,37 @@ test.describe("Room list filters and sort", () => {
|
|||||||
|
|
||||||
await primaryFilters.getByRole("option", { name: "Unread" }).click();
|
await primaryFilters.getByRole("option", { name: "Unread" }).click();
|
||||||
// only one room should be visible
|
// only one room should be visible
|
||||||
await expect(roomList.getByRole("gridcell", { name: "unread dm" })).toBeVisible();
|
await expect(roomList.getByRole("option", { name: "unread dm" })).toBeVisible();
|
||||||
await expect(roomList.getByRole("gridcell", { name: "unread room" })).toBeVisible();
|
await expect(roomList.getByRole("option", { name: "unread room" })).toBeVisible();
|
||||||
await expect.poll(() => roomList.locator("role=gridcell").count()).toBe(4);
|
await expect.poll(() => roomList.locator("role=option").count()).toBe(4);
|
||||||
await expect(primaryFilters).toMatchScreenshot("unread-primary-filters.png");
|
await expect(primaryFilters).toMatchScreenshot("unread-primary-filters.png");
|
||||||
|
|
||||||
await primaryFilters.getByRole("option", { name: "People" }).click();
|
await primaryFilters.getByRole("option", { name: "People" }).click();
|
||||||
await expect(roomList.getByRole("gridcell", { name: "unread dm" })).toBeVisible();
|
await expect(roomList.getByRole("option", { name: "unread dm" })).toBeVisible();
|
||||||
await expect(roomList.getByRole("gridcell", { name: "invited room" })).toBeVisible();
|
await expect(roomList.getByRole("option", { name: "invited room" })).toBeVisible();
|
||||||
await expect.poll(() => roomList.locator("role=gridcell").count()).toBe(2);
|
await expect.poll(() => roomList.locator("role=option").count()).toBe(2);
|
||||||
|
|
||||||
await primaryFilters.getByRole("option", { name: "Rooms" }).click();
|
await primaryFilters.getByRole("option", { name: "Rooms" }).click();
|
||||||
await expect(roomList.getByRole("gridcell", { name: "unread room" })).toBeVisible();
|
await expect(roomList.getByRole("option", { name: "unread room" })).toBeVisible();
|
||||||
await expect(roomList.getByRole("gridcell", { name: "favourite room" })).toBeVisible();
|
await expect(roomList.getByRole("option", { name: "favourite room" })).toBeVisible();
|
||||||
await expect(roomList.getByRole("gridcell", { name: "empty room" })).toBeVisible();
|
await expect(roomList.getByRole("option", { name: "empty room" })).toBeVisible();
|
||||||
await expect(roomList.getByRole("gridcell", { name: "room with mention" })).toBeVisible();
|
await expect(roomList.getByRole("option", { name: "room with mention" })).toBeVisible();
|
||||||
await expect(roomList.getByRole("gridcell", { name: "Low prio room" })).toBeVisible();
|
await expect(roomList.getByRole("option", { name: "Low prio room" })).toBeVisible();
|
||||||
await expect.poll(() => roomList.locator("role=gridcell").count()).toBe(5);
|
await expect.poll(() => roomList.locator("role=option").count()).toBe(5);
|
||||||
|
|
||||||
await getFilterExpandButton(page).click();
|
await getFilterExpandButton(page).click();
|
||||||
|
|
||||||
await primaryFilters.getByRole("option", { name: "Favourite" }).click();
|
await primaryFilters.getByRole("option", { name: "Favourite" }).click();
|
||||||
await expect(roomList.getByRole("gridcell", { name: "favourite room" })).toBeVisible();
|
await expect(roomList.getByRole("option", { name: "favourite room" })).toBeVisible();
|
||||||
await expect.poll(() => roomList.locator("role=gridcell").count()).toBe(1);
|
await expect.poll(() => roomList.locator("role=option").count()).toBe(1);
|
||||||
|
|
||||||
await primaryFilters.getByRole("option", { name: "Mentions" }).click();
|
await primaryFilters.getByRole("option", { name: "Mentions" }).click();
|
||||||
await expect(roomList.getByRole("gridcell", { name: "room with mention" })).toBeVisible();
|
await expect(roomList.getByRole("option", { name: "room with mention" })).toBeVisible();
|
||||||
await expect.poll(() => roomList.locator("role=gridcell").count()).toBe(1);
|
await expect.poll(() => roomList.locator("role=option").count()).toBe(1);
|
||||||
|
|
||||||
await primaryFilters.getByRole("option", { name: "Invites" }).click();
|
await primaryFilters.getByRole("option", { name: "Invites" }).click();
|
||||||
await expect(roomList.getByRole("gridcell", { name: "invited room" })).toBeVisible();
|
await expect(roomList.getByRole("option", { name: "invited room" })).toBeVisible();
|
||||||
await expect.poll(() => roomList.locator("role=gridcell").count()).toBe(1);
|
await expect.poll(() => roomList.locator("role=option").count()).toBe(1);
|
||||||
|
|
||||||
await getFilterCollapseButton(page).click();
|
await getFilterCollapseButton(page).click();
|
||||||
await expect(primaryFilters.locator("role=option").first()).toHaveText("Invites");
|
await expect(primaryFilters.locator("role=option").first()).toHaveText("Invites");
|
||||||
@@ -268,6 +269,7 @@ test.describe("Room list filters and sort", () => {
|
|||||||
{ tag: "@screenshot" },
|
{ tag: "@screenshot" },
|
||||||
async ({ page, app, bot }) => {
|
async ({ page, app, bot }) => {
|
||||||
const roomListView = getRoomList(page);
|
const roomListView = getRoomList(page);
|
||||||
|
const primaryFilters = getPrimaryFilters(page);
|
||||||
|
|
||||||
// Let's configure unread dm room so that we only get notification for mentions and keywords
|
// Let's configure unread dm room so that we only get notification for mentions and keywords
|
||||||
await app.viewRoomById(unReadDmId);
|
await app.viewRoomById(unReadDmId);
|
||||||
@@ -276,20 +278,20 @@ test.describe("Room list filters and sort", () => {
|
|||||||
await app.settings.closeDialog();
|
await app.settings.closeDialog();
|
||||||
|
|
||||||
// Let's open a room other than unread room or unread dm
|
// Let's open a room other than unread room or unread dm
|
||||||
await roomListView.getByRole("gridcell", { name: "Open room favourite room" }).click();
|
await roomListView.getByRole("option", { name: "Open room favourite room" }).click();
|
||||||
|
|
||||||
// Let's make the bot send a new message in both rooms
|
// Let's make the bot send a new message in both rooms
|
||||||
await bot.sendMessage(unReadDmId, "Hello!");
|
await bot.sendMessage(unReadDmId, "Hello!");
|
||||||
await bot.sendMessage(unReadRoomId, "Hello!");
|
await bot.sendMessage(unReadRoomId, "Hello!");
|
||||||
|
|
||||||
// Let's activate the unread filter now
|
// Let's activate the unread filter now
|
||||||
await page.getByRole("option", { name: "Unread" }).click();
|
await primaryFilters.getByRole("option", { name: "Unread" }).click();
|
||||||
|
|
||||||
// Unread filter should only show unread room and not unread dm!
|
// Unread filter should only show unread room and not unread dm!
|
||||||
const unreadDm = roomListView.getByRole("gridcell", { name: "Open room unread room" });
|
const unreadDm = roomListView.getByRole("option", { name: "Open room unread room" });
|
||||||
await expect(unreadDm).toBeVisible();
|
await expect(unreadDm).toBeVisible();
|
||||||
await expect(unreadDm).toMatchScreenshot("unread-dm.png");
|
await expect(unreadDm).toMatchScreenshot("unread-dm.png");
|
||||||
await expect(roomListView.getByRole("gridcell", { name: "Open room unread dm" })).not.toBeVisible();
|
await expect(roomListView.getByRole("option", { name: "Open room unread dm" })).not.toBeVisible();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -299,7 +301,7 @@ test.describe("Room list filters and sort", () => {
|
|||||||
await getRoomOptionsMenu(page).click();
|
await getRoomOptionsMenu(page).click();
|
||||||
await page.getByRole("menuitemradio", { name: "A-Z" }).click();
|
await page.getByRole("menuitemradio", { name: "A-Z" }).click();
|
||||||
|
|
||||||
await expect(roomListView.getByRole("gridcell").first()).toHaveText(/empty room/);
|
await expect(roomListView.getByRole("option").first()).toHaveText(/empty room/);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should move room to the top on message when sorting by activity", async ({ page, bot }) => {
|
test("should move room to the top on message when sorting by activity", async ({ page, bot }) => {
|
||||||
@@ -307,7 +309,7 @@ test.describe("Room list filters and sort", () => {
|
|||||||
|
|
||||||
await bot.sendMessage(unReadDmId, "Hello!");
|
await bot.sendMessage(unReadDmId, "Hello!");
|
||||||
|
|
||||||
await expect(roomListView.getByRole("gridcell").first()).toHaveText(/unread dm/);
|
await expect(roomListView.getByRole("option").first()).toHaveText(/unread dm/);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ test.describe("Room list panel", () => {
|
|||||||
test("should render the room list panel", { tag: "@screenshot" }, async ({ page, app, user }) => {
|
test("should render the room list panel", { tag: "@screenshot" }, async ({ page, app, user }) => {
|
||||||
const roomListView = getRoomListView(page);
|
const roomListView = getRoomListView(page);
|
||||||
// Wait for the last room to be visible
|
// Wait for the last room to be visible
|
||||||
await expect(roomListView.getByRole("gridcell", { name: "Open room room19" })).toBeVisible();
|
await expect(roomListView.getByRole("option", { name: "Open room room19" })).toBeVisible();
|
||||||
await expect(roomListView).toMatchScreenshot("room-list-panel.png");
|
await expect(roomListView).toMatchScreenshot("room-list-panel.png");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -43,31 +43,35 @@ test.describe("Room list", () => {
|
|||||||
|
|
||||||
test("should render the room list", { tag: "@screenshot" }, async ({ page, app, user }) => {
|
test("should render the room list", { tag: "@screenshot" }, async ({ page, app, user }) => {
|
||||||
const roomListView = getRoomList(page);
|
const roomListView = getRoomList(page);
|
||||||
await expect(roomListView.getByRole("gridcell", { name: "Open room room29" })).toBeVisible();
|
await expect(roomListView.getByRole("option", { name: "Open room room29" })).toBeVisible();
|
||||||
await expect(roomListView).toMatchScreenshot("room-list.png");
|
await expect(roomListView).toMatchScreenshot("room-list.png");
|
||||||
|
|
||||||
// Put focus on the room list
|
// Put focus on the room list
|
||||||
await roomListView.getByRole("gridcell", { name: "Open room room29" }).click();
|
await roomListView.getByRole("option", { name: "Open room room29" }).click();
|
||||||
// Scroll to the end of the room list
|
// Scroll to the end of the room list
|
||||||
await app.scrollListToBottom(page.locator(".mx_RoomList_List"));
|
await app.scrollListToBottom(roomListView);
|
||||||
|
|
||||||
|
// scrollListToBottom seems to leave the mouse hovered over the list, move it away.
|
||||||
|
await page.getByRole("button", { name: "User menu" }).hover();
|
||||||
|
|
||||||
await expect(roomListView).toMatchScreenshot("room-list-scrolled.png");
|
await expect(roomListView).toMatchScreenshot("room-list-scrolled.png");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should open the room when it is clicked", async ({ page, app, user }) => {
|
test("should open the room when it is clicked", async ({ page, app, user }) => {
|
||||||
const roomListView = getRoomList(page);
|
const roomListView = getRoomList(page);
|
||||||
await roomListView.getByRole("gridcell", { name: "Open room room29" }).click();
|
await roomListView.getByRole("option", { name: "Open room room29" }).click();
|
||||||
await expect(page.getByRole("heading", { name: "room29", level: 1 })).toBeVisible();
|
await expect(page.getByRole("heading", { name: "room29", level: 1 })).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should open the context menu", { tag: "@screenshot" }, async ({ page, app, user }) => {
|
test("should open the context menu", { tag: "@screenshot" }, async ({ page, app, user }) => {
|
||||||
const roomListView = getRoomList(page);
|
const roomListView = getRoomList(page);
|
||||||
await roomListView.getByRole("gridcell", { name: "Open room room29" }).click({ button: "right" });
|
await roomListView.getByRole("option", { name: "Open room room29" }).click({ button: "right" });
|
||||||
await expect(page.getByRole("menu", { name: "More Options" })).toBeVisible();
|
await expect(page.getByRole("menu", { name: "More Options" })).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should open the more options menu", { tag: "@screenshot" }, async ({ page, app, user }) => {
|
test("should open the more options menu", { tag: "@screenshot" }, async ({ page, app, user }) => {
|
||||||
const roomListView = getRoomList(page);
|
const roomListView = getRoomList(page);
|
||||||
const roomItem = roomListView.getByRole("gridcell", { name: "Open room room29" });
|
const roomItem = roomListView.getByRole("option", { name: "Open room room29" });
|
||||||
await roomItem.hover();
|
await roomItem.hover();
|
||||||
|
|
||||||
await expect(roomItem).toMatchScreenshot("room-list-item-hover.png");
|
await expect(roomItem).toMatchScreenshot("room-list-item-hover.png");
|
||||||
@@ -97,7 +101,7 @@ test.describe("Room list", () => {
|
|||||||
test("should open the notification options menu", { tag: "@screenshot" }, async ({ page, app, user }) => {
|
test("should open the notification options menu", { tag: "@screenshot" }, async ({ page, app, user }) => {
|
||||||
const roomListView = getRoomList(page);
|
const roomListView = getRoomList(page);
|
||||||
|
|
||||||
const roomItem = roomListView.getByRole("gridcell", { name: "Open room room29" });
|
const roomItem = roomListView.getByRole("option", { name: "Open room room29" });
|
||||||
await roomItem.hover();
|
await roomItem.hover();
|
||||||
|
|
||||||
await expect(roomItem).toMatchScreenshot("room-list-item-hover.png");
|
await expect(roomItem).toMatchScreenshot("room-list-item-hover.png");
|
||||||
@@ -117,10 +121,10 @@ test.describe("Room list", () => {
|
|||||||
await expect(roomItem.getByTestId("notification-decoration")).not.toBeVisible();
|
await expect(roomItem.getByTestId("notification-decoration")).not.toBeVisible();
|
||||||
|
|
||||||
// Put focus on the room list
|
// Put focus on the room list
|
||||||
await roomListView.getByRole("gridcell", { name: "Open room room28" }).click();
|
await roomListView.getByRole("option", { name: "Open room room28" }).click();
|
||||||
|
|
||||||
// Scroll to the end of the room list
|
// Scroll to the end of the room list
|
||||||
await app.scrollListToBottom(page.locator(".mx_RoomList_List"));
|
await app.scrollListToBottom(roomListView);
|
||||||
|
|
||||||
// The room decoration should have the muted icon
|
// The room decoration should have the muted icon
|
||||||
await expect(roomItem.getByTestId("notification-decoration")).toBeVisible();
|
await expect(roomItem.getByTestId("notification-decoration")).toBeVisible();
|
||||||
@@ -139,25 +143,25 @@ test.describe("Room list", () => {
|
|||||||
test("should scroll to the current room", async ({ page, app, user }) => {
|
test("should scroll to the current room", async ({ page, app, user }) => {
|
||||||
const roomListView = getRoomList(page);
|
const roomListView = getRoomList(page);
|
||||||
// Put focus on the room list
|
// Put focus on the room list
|
||||||
await roomListView.getByRole("gridcell", { name: "Open room room29" }).click();
|
await roomListView.getByRole("option", { name: "Open room room29" }).click();
|
||||||
// Scroll to the end of the room list
|
// Scroll to the end of the room list
|
||||||
await app.scrollListToBottom(page.locator(".mx_RoomList_List"));
|
await app.scrollListToBottom(roomListView);
|
||||||
|
|
||||||
await expect(roomListView.getByRole("gridcell", { name: "Open room room0" })).toBeVisible();
|
await expect(roomListView.getByRole("option", { name: "Open room room0" })).toBeVisible();
|
||||||
await roomListView.getByRole("gridcell", { name: "Open room room0" }).click();
|
await roomListView.getByRole("option", { name: "Open room room0" }).click();
|
||||||
|
|
||||||
const filters = page.getByRole("listbox", { name: "Room list filters" });
|
const filters = page.getByRole("listbox", { name: "Room list filters" });
|
||||||
await filters.getByRole("option", { name: "People" }).click();
|
await filters.getByRole("option", { name: "People" }).click();
|
||||||
await expect(roomListView.getByRole("gridcell", { name: "Open room room0" })).not.toBeVisible();
|
await expect(roomListView.getByRole("option", { name: "Open room room0" })).not.toBeVisible();
|
||||||
|
|
||||||
await filters.getByRole("option", { name: "People" }).click();
|
await filters.getByRole("option", { name: "People" }).click();
|
||||||
await expect(roomListView.getByRole("gridcell", { name: "Open room room0" })).toBeVisible();
|
await expect(roomListView.getByRole("option", { name: "Open room room0" })).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test.describe("Shortcuts", () => {
|
test.describe("Shortcuts", () => {
|
||||||
test("should select the next room", async ({ page, app, user }) => {
|
test("should select the next room", async ({ page, app, user }) => {
|
||||||
const roomListView = getRoomList(page);
|
const roomListView = getRoomList(page);
|
||||||
await roomListView.getByRole("gridcell", { name: "Open room room29" }).click();
|
await roomListView.getByRole("option", { name: "Open room room29" }).click();
|
||||||
await page.keyboard.press("Alt+ArrowDown");
|
await page.keyboard.press("Alt+ArrowDown");
|
||||||
|
|
||||||
await expect(page.getByRole("heading", { name: "room28", level: 1 })).toBeVisible();
|
await expect(page.getByRole("heading", { name: "room28", level: 1 })).toBeVisible();
|
||||||
@@ -165,7 +169,7 @@ test.describe("Room list", () => {
|
|||||||
|
|
||||||
test("should select the previous room", async ({ page, app, user }) => {
|
test("should select the previous room", async ({ page, app, user }) => {
|
||||||
const roomListView = getRoomList(page);
|
const roomListView = getRoomList(page);
|
||||||
await roomListView.getByRole("gridcell", { name: "Open room room28" }).click();
|
await roomListView.getByRole("option", { name: "Open room room28" }).click();
|
||||||
await page.keyboard.press("Alt+ArrowUp");
|
await page.keyboard.press("Alt+ArrowUp");
|
||||||
|
|
||||||
await expect(page.getByRole("heading", { name: "room29", level: 1 })).toBeVisible();
|
await expect(page.getByRole("heading", { name: "room29", level: 1 })).toBeVisible();
|
||||||
@@ -173,7 +177,7 @@ test.describe("Room list", () => {
|
|||||||
|
|
||||||
test("should select the last room", async ({ page, app, user }) => {
|
test("should select the last room", async ({ page, app, user }) => {
|
||||||
const roomListView = getRoomList(page);
|
const roomListView = getRoomList(page);
|
||||||
await roomListView.getByRole("gridcell", { name: "Open room room29" }).click();
|
await roomListView.getByRole("option", { name: "Open room room29" }).click();
|
||||||
await page.keyboard.press("Alt+ArrowUp");
|
await page.keyboard.press("Alt+ArrowUp");
|
||||||
|
|
||||||
await expect(page.getByRole("heading", { name: "room0", level: 1 })).toBeVisible();
|
await expect(page.getByRole("heading", { name: "room0", level: 1 })).toBeVisible();
|
||||||
@@ -187,7 +191,10 @@ test.describe("Room list", () => {
|
|||||||
await bot.joinRoom(roomId);
|
await bot.joinRoom(roomId);
|
||||||
await bot.sendMessage(roomId, "I am a robot. Beep.");
|
await bot.sendMessage(roomId, "I am a robot. Beep.");
|
||||||
|
|
||||||
await roomListView.getByRole("gridcell", { name: "Open room room20" }).click();
|
await roomListView.getByRole("option", { name: "Open room room20" }).click();
|
||||||
|
|
||||||
|
// Make sure the room with the unread is visible before we press the keyboard action to select it
|
||||||
|
await expect(roomListView.getByRole("option", { name: "1 notification" })).toBeVisible();
|
||||||
|
|
||||||
await page.keyboard.press("Alt+Shift+ArrowDown");
|
await page.keyboard.press("Alt+Shift+ArrowDown");
|
||||||
|
|
||||||
@@ -199,8 +206,8 @@ test.describe("Room list", () => {
|
|||||||
test("should navigate to the room list", async ({ page, app, user }) => {
|
test("should navigate to the room list", async ({ page, app, user }) => {
|
||||||
const roomListView = getRoomList(page);
|
const roomListView = getRoomList(page);
|
||||||
|
|
||||||
const room29 = roomListView.getByRole("gridcell", { name: "Open room room29" });
|
const room29 = roomListView.getByRole("option", { name: "Open room room29" });
|
||||||
const room28 = roomListView.getByRole("gridcell", { name: "Open room room28" });
|
const room28 = roomListView.getByRole("option", { name: "Open room room28" });
|
||||||
|
|
||||||
// open the room
|
// open the room
|
||||||
await room29.click();
|
await room29.click();
|
||||||
@@ -219,7 +226,7 @@ test.describe("Room list", () => {
|
|||||||
|
|
||||||
test("should navigate to the notification menu", async ({ page, app, user }) => {
|
test("should navigate to the notification menu", async ({ page, app, user }) => {
|
||||||
const roomListView = getRoomList(page);
|
const roomListView = getRoomList(page);
|
||||||
const room29 = roomListView.getByRole("gridcell", { name: "Open room room29" });
|
const room29 = roomListView.getByRole("option", { name: "Open room room29" });
|
||||||
const moreButton = room29.getByRole("button", { name: "More options" });
|
const moreButton = room29.getByRole("button", { name: "More options" });
|
||||||
const notificationButton = room29.getByRole("button", { name: "Notification options" });
|
const notificationButton = room29.getByRole("button", { name: "Notification options" });
|
||||||
|
|
||||||
@@ -258,7 +265,7 @@ test.describe("Room list", () => {
|
|||||||
await page.getByRole("button", { name: "User menu" }).focus();
|
await page.getByRole("button", { name: "User menu" }).focus();
|
||||||
|
|
||||||
const roomListView = getRoomList(page);
|
const roomListView = getRoomList(page);
|
||||||
const publicRoom = roomListView.getByRole("gridcell", { name: "public room" });
|
const publicRoom = roomListView.getByRole("option", { name: "public room" });
|
||||||
|
|
||||||
await expect(publicRoom).toBeVisible();
|
await expect(publicRoom).toBeVisible();
|
||||||
await expect(publicRoom).toMatchScreenshot("room-list-item-public.png");
|
await expect(publicRoom).toMatchScreenshot("room-list-item-public.png");
|
||||||
@@ -268,7 +275,7 @@ test.describe("Room list", () => {
|
|||||||
// @ts-ignore Visibility enum is not accessible
|
// @ts-ignore Visibility enum is not accessible
|
||||||
await app.client.createRoom({ name: "low priority room", visibility: "public" });
|
await app.client.createRoom({ name: "low priority room", visibility: "public" });
|
||||||
const roomListView = getRoomList(page);
|
const roomListView = getRoomList(page);
|
||||||
const publicRoom = roomListView.getByRole("gridcell", { name: "low priority room" });
|
const publicRoom = roomListView.getByRole("option", { name: "low priority room" });
|
||||||
|
|
||||||
// Make room low priority
|
// Make room low priority
|
||||||
await publicRoom.hover();
|
await publicRoom.hover();
|
||||||
@@ -293,7 +300,7 @@ test.describe("Room list", () => {
|
|||||||
await page.getByRole("button", { name: "Create video room" }).click();
|
await page.getByRole("button", { name: "Create video room" }).click();
|
||||||
|
|
||||||
const roomListView = getRoomList(page);
|
const roomListView = getRoomList(page);
|
||||||
const videoRoom = roomListView.getByRole("gridcell", { name: "video room" });
|
const videoRoom = roomListView.getByRole("option", { name: "video room" });
|
||||||
|
|
||||||
// focus the user menu to avoid to have hover decoration
|
// focus the user menu to avoid to have hover decoration
|
||||||
await page.getByRole("button", { name: "User menu" }).focus();
|
await page.getByRole("button", { name: "User menu" }).focus();
|
||||||
@@ -312,7 +319,7 @@ test.describe("Room list", () => {
|
|||||||
invite: [user.userId],
|
invite: [user.userId],
|
||||||
is_direct: true,
|
is_direct: true,
|
||||||
});
|
});
|
||||||
const invitedRoom = roomListView.getByRole("gridcell", { name: "invited room" });
|
const invitedRoom = roomListView.getByRole("option", { name: "invited room" });
|
||||||
await expect(invitedRoom).toBeVisible();
|
await expect(invitedRoom).toBeVisible();
|
||||||
await expect(invitedRoom).toMatchScreenshot("room-list-item-invited.png");
|
await expect(invitedRoom).toMatchScreenshot("room-list-item-invited.png");
|
||||||
});
|
});
|
||||||
@@ -327,7 +334,7 @@ test.describe("Room list", () => {
|
|||||||
await bot.sendMessage(roomId, "I am a robot. Beep.");
|
await bot.sendMessage(roomId, "I am a robot. Beep.");
|
||||||
await bot.sendMessage(roomId, "I am a robot. Beep.");
|
await bot.sendMessage(roomId, "I am a robot. Beep.");
|
||||||
|
|
||||||
const room = roomListView.getByRole("gridcell", { name: "2 notifications" });
|
const room = roomListView.getByRole("option", { name: "2 notifications" });
|
||||||
await expect(room).toBeVisible();
|
await expect(room).toBeVisible();
|
||||||
await expect(room.getByTestId("notification-decoration")).toHaveText("2");
|
await expect(room.getByTestId("notification-decoration")).toHaveText("2");
|
||||||
await expect(room).toMatchScreenshot("room-list-item-notification.png");
|
await expect(room).toMatchScreenshot("room-list-item-notification.png");
|
||||||
@@ -358,7 +365,7 @@ test.describe("Room list", () => {
|
|||||||
);
|
);
|
||||||
await bot.sendMessage(roomId, "I am a robot. Beep.");
|
await bot.sendMessage(roomId, "I am a robot. Beep.");
|
||||||
|
|
||||||
const room = roomListView.getByRole("gridcell", { name: "mention" });
|
const room = roomListView.getByRole("option", { name: "mention" });
|
||||||
await expect(room).toBeVisible();
|
await expect(room).toBeVisible();
|
||||||
await expect(room).toMatchScreenshot("room-list-item-mention.png");
|
await expect(room).toMatchScreenshot("room-list-item-mention.png");
|
||||||
});
|
});
|
||||||
@@ -379,7 +386,7 @@ test.describe("Room list", () => {
|
|||||||
await bot.joinRoom(roomId);
|
await bot.joinRoom(roomId);
|
||||||
await bot.sendMessage(roomId, "I am a robot. Beep.");
|
await bot.sendMessage(roomId, "I am a robot. Beep.");
|
||||||
|
|
||||||
const room = roomListView.getByRole("gridcell", { name: "activity" });
|
const room = roomListView.getByRole("option", { name: "activity" });
|
||||||
await expect(room.getByText("I am a robot. Beep.")).toBeVisible();
|
await expect(room.getByText("I am a robot. Beep.")).toBeVisible();
|
||||||
await expect(room).toMatchScreenshot("room-list-item-message-preview.png");
|
await expect(room).toMatchScreenshot("room-list-item-message-preview.png");
|
||||||
});
|
});
|
||||||
@@ -406,7 +413,7 @@ test.describe("Room list", () => {
|
|||||||
await app.viewRoomById(otherRoomId);
|
await app.viewRoomById(otherRoomId);
|
||||||
await bot.sendMessage(roomId, "I am a robot. Beep.");
|
await bot.sendMessage(roomId, "I am a robot. Beep.");
|
||||||
|
|
||||||
const room = roomListView.getByRole("gridcell", { name: "activity" });
|
const room = roomListView.getByRole("option", { name: "activity" });
|
||||||
await expect(room.getByTestId("notification-decoration")).toBeVisible();
|
await expect(room.getByTestId("notification-decoration")).toBeVisible();
|
||||||
await expect(room).toMatchScreenshot("room-list-item-activity.png");
|
await expect(room).toMatchScreenshot("room-list-item-activity.png");
|
||||||
});
|
});
|
||||||
@@ -418,7 +425,7 @@ test.describe("Room list", () => {
|
|||||||
await app.client.inviteUser(roomId, bot.credentials.userId);
|
await app.client.inviteUser(roomId, bot.credentials.userId);
|
||||||
await bot.joinRoom(roomId);
|
await bot.joinRoom(roomId);
|
||||||
|
|
||||||
const room = roomListView.getByRole("gridcell", { name: "mark as unread" });
|
const room = roomListView.getByRole("option", { name: "mark as unread" });
|
||||||
await room.hover();
|
await room.hover();
|
||||||
await room.getByRole("button", { name: "More Options" }).click();
|
await room.getByRole("button", { name: "More Options" }).click();
|
||||||
await page.getByRole("menuitem", { name: "mark as unread" }).click();
|
await page.getByRole("menuitem", { name: "mark as unread" }).click();
|
||||||
@@ -441,7 +448,7 @@ test.describe("Room list", () => {
|
|||||||
await page.getByText("Off").click();
|
await page.getByText("Off").click();
|
||||||
await app.settings.closeDialog();
|
await app.settings.closeDialog();
|
||||||
|
|
||||||
const room = roomListView.getByRole("gridcell", { name: "silent" });
|
const room = roomListView.getByRole("option", { name: "silent" });
|
||||||
await expect(room.getByTestId("notification-decoration")).toBeVisible();
|
await expect(room.getByTestId("notification-decoration")).toBeVisible();
|
||||||
await expect(room).toMatchScreenshot("room-list-item-silent.png");
|
await expect(room).toMatchScreenshot("room-list-item-silent.png");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -95,10 +95,6 @@ test.describe("OIDC Native", { tag: ["@no-firefox", "@no-webkit"] }, () => {
|
|||||||
const result = await mas.manage("kill-sessions", userId);
|
const result = await mas.manage("kill-sessions", userId);
|
||||||
expect(result.output).toContain("Ended 1 active OAuth 2.0 session");
|
expect(result.output).toContain("Ended 1 active OAuth 2.0 session");
|
||||||
|
|
||||||
// Workaround for Synapse's 2 minute cache on MAS token validity
|
|
||||||
// (https://github.com/element-hq/synapse/pull/18231)
|
|
||||||
await homeserver.restart();
|
|
||||||
|
|
||||||
await page.goto("http://localhost:8080");
|
await page.goto("http://localhost:8080");
|
||||||
await expect(
|
await expect(
|
||||||
page.getByText("For security, this session has been signed out. Please sign in again."),
|
page.getByText("For security, this session has been signed out. Please sign in again."),
|
||||||
|
|||||||
@@ -100,3 +100,51 @@ test.describe("permalinks", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.describe("triple-click message selection", () => {
|
||||||
|
test.use({
|
||||||
|
displayName: "Alice",
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should select entire message line when triple-clicking on message with pills", async ({
|
||||||
|
page,
|
||||||
|
app,
|
||||||
|
user,
|
||||||
|
bot,
|
||||||
|
}) => {
|
||||||
|
await bot.prepareClient();
|
||||||
|
|
||||||
|
const roomId = await app.client.createRoom({ name: "Test Room" });
|
||||||
|
await app.client.inviteUser(roomId, bot.credentials.userId);
|
||||||
|
await app.viewRoomByName("Test Room");
|
||||||
|
|
||||||
|
// Send a message with user and room pills
|
||||||
|
await app.client.sendMessage(
|
||||||
|
roomId,
|
||||||
|
`Testing triple-click message selection. ` +
|
||||||
|
`User: ${permalinkPrefix}${bot.credentials.userId}, ` +
|
||||||
|
`Room: ${permalinkPrefix}${roomId}, ` +
|
||||||
|
`Message: ${permalinkPrefix}${roomId}/$dummy-event, ` +
|
||||||
|
`and @room mention.`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const timeline = page.locator(".mx_RoomView_timeline");
|
||||||
|
const messageTile = timeline.locator(".mx_EventTile").last();
|
||||||
|
|
||||||
|
// Triple-click on the message body to select its entire content
|
||||||
|
const messageBody = messageTile.locator(".mx_EventTile_body");
|
||||||
|
await messageBody.click({ clickCount: 3 });
|
||||||
|
|
||||||
|
// Get the expected text content of the message, including pills
|
||||||
|
const expectedText = await messageBody.innerText();
|
||||||
|
|
||||||
|
// Get the currently selected text from the page
|
||||||
|
const selectedText = await page.evaluate(() => {
|
||||||
|
const selection = window.getSelection();
|
||||||
|
return selection ? selection.toString().trim() : "";
|
||||||
|
});
|
||||||
|
|
||||||
|
// Verify that the selected text exactly matches the message content
|
||||||
|
expect(selectedText).toBe(expectedText);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
|||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { type Download, 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 { viewRoomSummaryByName } from "./utils";
|
import { viewRoomSummaryByName } from "./utils";
|
||||||
@@ -189,23 +189,13 @@ test.describe("FilePanel", () => {
|
|||||||
|
|
||||||
const link = imageBody.locator(".mx_MFileBody_download a");
|
const link = imageBody.locator(".mx_MFileBody_download a");
|
||||||
|
|
||||||
const newPagePromise = context.waitForEvent("page");
|
const downloadPromise = page.waitForEvent("download");
|
||||||
|
|
||||||
const downloadPromise = new Promise<Download>((resolve) => {
|
|
||||||
page.once("download", resolve);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Click the anchor link (not the image itself)
|
// Click the anchor link (not the image itself)
|
||||||
await link.click();
|
await link.click();
|
||||||
|
|
||||||
const newPage = await newPagePromise;
|
const download = await downloadPromise;
|
||||||
// XXX: Clicking the link opens the image in a new tab on some browsers rather than downloading
|
expect(download.suggestedFilename()).toBe("riot.png");
|
||||||
await expect(newPage)
|
|
||||||
.toHaveURL(/.+\/_matrix\/media\/\w+\/download\/localhost\/\w+/)
|
|
||||||
.catch(async () => {
|
|
||||||
const download = await downloadPromise;
|
|
||||||
expect(download.suggestedFilename()).toBe("riot.png");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -37,11 +37,8 @@ test.describe("Room Header", () => {
|
|||||||
await expect(header.locator(".mx_FacePile")).toBeVisible();
|
await expect(header.locator(".mx_FacePile")).toBeVisible();
|
||||||
|
|
||||||
// There should be both a voice and a video call button
|
// There should be both a voice and a video call button
|
||||||
// but they'll be disabled
|
await expect(header.getByRole("button", { name: "Video call" })).toBeVisible();
|
||||||
const callButtons = header.getByRole("button", { name: "There's no one here to call" });
|
await expect(header.getByRole("button", { name: "Voice call" })).toBeVisible();
|
||||||
await expect(callButtons).toHaveCount(2);
|
|
||||||
await expect(callButtons.first()).toBeVisible();
|
|
||||||
await expect(callButtons.last()).toBeVisible();
|
|
||||||
|
|
||||||
await expect(header.getByRole("button", { name: "Threads" })).toBeVisible();
|
await expect(header.getByRole("button", { name: "Threads" })).toBeVisible();
|
||||||
await expect(header.getByRole("button", { name: "Notifications" })).toBeVisible();
|
await expect(header.getByRole("button", { name: "Notifications" })).toBeVisible();
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ class Helpers {
|
|||||||
* Return the system theme toggle
|
* Return the system theme toggle
|
||||||
*/
|
*/
|
||||||
getMatchSystemThemeCheckbox() {
|
getMatchSystemThemeCheckbox() {
|
||||||
return this.getThemePanel().getByRole("checkbox", { name: "Match system theme" });
|
return this.getThemePanel().getByRole("switch", { name: "Match system theme" });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -219,7 +219,7 @@ class Helpers {
|
|||||||
* Return the compact layout checkbox
|
* Return the compact layout checkbox
|
||||||
*/
|
*/
|
||||||
getCompactLayoutCheckbox() {
|
getCompactLayoutCheckbox() {
|
||||||
return this.getMessageLayoutPanel().getByRole("checkbox", { name: "Show compact text and messages" });
|
return this.getMessageLayoutPanel().getByRole("switch", { name: "Show compact text and messages" });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ test.describe("Encryption tab", () => {
|
|||||||
await verifySession(app, recoveryKey.encodedPrivateKey);
|
await verifySession(app, recoveryKey.encodedPrivateKey);
|
||||||
await util.openEncryptionTab();
|
await util.openEncryptionTab();
|
||||||
|
|
||||||
await page.getByRole("checkbox", { name: "Allow key storage" }).click();
|
await page.getByRole("switch", { name: "Allow key storage" }).click();
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole("heading", { name: "Are you sure you want to turn off key storage and delete it?" }),
|
page.getByRole("heading", { name: "Are you sure you want to turn off key storage and delete it?" }),
|
||||||
@@ -136,7 +136,7 @@ test.describe("Encryption tab", () => {
|
|||||||
|
|
||||||
await page.getByRole("button", { name: "Delete key storage" }).click();
|
await page.getByRole("button", { name: "Delete key storage" }).click();
|
||||||
|
|
||||||
await expect(page.getByRole("checkbox", { name: "Allow key storage" })).not.toBeChecked();
|
await expect(page.getByRole("switch", { name: "Allow key storage" })).not.toBeChecked();
|
||||||
|
|
||||||
for (const prom of deleteRequestPromises) {
|
for (const prom of deleteRequestPromises) {
|
||||||
const request = await prom;
|
const request = await prom;
|
||||||
|
|||||||
@@ -18,13 +18,14 @@ test.describe("share from URL", () => {
|
|||||||
|
|
||||||
test("should share message when users navigates to share URL", async ({ page, user, room, app }) => {
|
test("should share message when users navigates to share URL", async ({ page, user, room, app }) => {
|
||||||
await page.goto("/#/share?msg=Hello+world");
|
await page.goto("/#/share?msg=Hello+world");
|
||||||
|
const dialog = page.getByRole("dialog", { name: "Forward message" });
|
||||||
// The forward message dialog doesn't update as new infomation arrives via sync, which means sometimes
|
// The forward message dialog doesn't update as new infomation arrives via sync, which means sometimes
|
||||||
// this is just says, "Empty room". For the same reason, we can't reliably write a test for loading the
|
// this is just says, "Empty room". For the same reason, we can't reliably write a test for loading the
|
||||||
// app straight away with a /#/share url as the room doesn't appear until the client syncs.]
|
// app straight away with a /#/share url as the room doesn't appear until the client syncs.]
|
||||||
// Ideally we should fix the forward dialog to update and eliminate races, until then, there is only one
|
// Ideally we should fix the forward dialog to update and eliminate races, until then, there is only one
|
||||||
// room so we click the first button.
|
// room so we click the first button.
|
||||||
await page.getByRole("listitem" /*, { name: "A test room" }*/).getByRole("button", { name: "Send" }).click();
|
await dialog.getByRole("listitem" /*, { name: "A test room" }*/).getByRole("button", { name: "Send" }).click();
|
||||||
await page.keyboard.press("Escape");
|
await dialog.getByRole("button", { name: "Close" }).click();
|
||||||
await app.viewRoomByName("A test room");
|
await app.viewRoomByName("A test room");
|
||||||
const lastMessage = page.locator(".mx_RoomView_MessageList .mx_EventTile_last");
|
const lastMessage = page.locator(".mx_RoomView_MessageList .mx_EventTile_last");
|
||||||
await expect(lastMessage).toBeVisible();
|
await expect(lastMessage).toBeVisible();
|
||||||
|
|||||||
@@ -908,23 +908,37 @@ test.describe("Timeline", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should be able to hide an image", { tag: "@screenshot" }, async ({ page, app, room, context }) => {
|
test(
|
||||||
await app.viewRoomById(room.roomId);
|
"should be able to hide an image",
|
||||||
await sendImage(app.client, room.roomId, NEW_AVATAR);
|
{ tag: "@screenshot" },
|
||||||
await app.timeline.scrollToBottom();
|
async ({ page, app, homeserver, room, context }) => {
|
||||||
const imgTile = page.locator(".mx_MImageBody").first();
|
await app.viewRoomById(room.roomId);
|
||||||
await expect(imgTile).toBeVisible();
|
|
||||||
await imgTile.hover();
|
|
||||||
await page.getByRole("button", { name: "Hide" }).click();
|
|
||||||
|
|
||||||
// Check that the image is now hidden.
|
const bot = new Bot(page, homeserver, {});
|
||||||
await expect(page.getByRole("button", { name: "Show image" })).toBeVisible();
|
await bot.prepareClient();
|
||||||
});
|
await app.client.inviteUser(room.roomId, bot.credentials.userId);
|
||||||
|
|
||||||
test("should be able to hide a video", async ({ page, app, room, context }) => {
|
await sendImage(bot, room.roomId, NEW_AVATAR);
|
||||||
|
await app.timeline.scrollToBottom();
|
||||||
|
const imgTile = page.locator(".mx_MImageBody").first();
|
||||||
|
await expect(imgTile).toBeVisible();
|
||||||
|
await imgTile.hover();
|
||||||
|
await page.getByRole("button", { name: "Hide" }).click();
|
||||||
|
|
||||||
|
// Check that the image is now hidden.
|
||||||
|
await expect(page.getByRole("button", { name: "Show image" })).toBeVisible();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
test("should be able to hide a video", async ({ page, app, homeserver, room, context }) => {
|
||||||
await app.viewRoomById(room.roomId);
|
await app.viewRoomById(room.roomId);
|
||||||
const upload = await app.client.uploadContent(VIDEO_FILE, { name: "bbb.webm", type: "video/webm" });
|
|
||||||
await app.client.sendEvent(room.roomId, null, "m.room.message" as EventType, {
|
const bot = new Bot(page, homeserver, {});
|
||||||
|
await bot.prepareClient();
|
||||||
|
await app.client.inviteUser(room.roomId, bot.credentials.userId);
|
||||||
|
|
||||||
|
const upload = await bot.uploadContent(VIDEO_FILE, { name: "bbb.webm", type: "video/webm" });
|
||||||
|
await bot.sendEvent(room.roomId, null, "m.room.message" as EventType, {
|
||||||
msgtype: "m.video" as MsgType,
|
msgtype: "m.video" as MsgType,
|
||||||
body: "bbb.webm",
|
body: "bbb.webm",
|
||||||
url: upload.content_uri,
|
url: upload.content_uri,
|
||||||
|
|||||||
@@ -1,38 +1,49 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024-2025 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 OR LicenseRef-Element-Commercial
|
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 { MatrixAuthenticationServiceContainer } from "@element-hq/element-web-playwright-common/lib/testcontainers";
|
import { MatrixAuthenticationServiceContainer } from "../../../testcontainers/mas.ts";
|
||||||
|
|
||||||
import { type Fixtures } from "../../../element-web-test.ts";
|
import { type Fixtures } from "../../../element-web-test.ts";
|
||||||
|
|
||||||
export const masHomeserver: Fixtures = {
|
export const masHomeserver: Fixtures = {
|
||||||
mas: [
|
mas: [
|
||||||
async ({ _homeserver: homeserver, logger, network, postgres, mailpit }, use) => {
|
async ({ _homeserver: homeserver, logger, network, postgres, mailpit }, use) => {
|
||||||
const config = {
|
const secret = "AnotherRandomSecret";
|
||||||
clients: [
|
|
||||||
{
|
|
||||||
client_id: "0000000000000000000SYNAPSE",
|
|
||||||
client_auth_method: "client_secret_basic",
|
|
||||||
client_secret: "SomeRandomSecret",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
matrix: {
|
|
||||||
homeserver: "localhost",
|
|
||||||
secret: "AnotherRandomSecret",
|
|
||||||
endpoint: "http://homeserver:8008",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
|
const limits = { burst: 10, per_second: 10 };
|
||||||
const container = await new MatrixAuthenticationServiceContainer(postgres)
|
const container = await new MatrixAuthenticationServiceContainer(postgres)
|
||||||
.withNetwork(network)
|
.withNetwork(network)
|
||||||
.withNetworkAliases("mas")
|
.withNetworkAliases("mas")
|
||||||
.withLogConsumer(logger.getConsumer("mas"))
|
.withLogConsumer(logger.getConsumer("mas"))
|
||||||
.withConfig(config)
|
.withConfig({
|
||||||
|
matrix: {
|
||||||
|
kind: "synapse",
|
||||||
|
homeserver: "localhost",
|
||||||
|
secret,
|
||||||
|
endpoint: "http://homeserver:8008",
|
||||||
|
},
|
||||||
|
rate_limiting: {
|
||||||
|
login: {
|
||||||
|
per_ip: limits,
|
||||||
|
per_account: limits,
|
||||||
|
},
|
||||||
|
registration: limits,
|
||||||
|
email_authentication: {
|
||||||
|
per_ip: limits,
|
||||||
|
per_address: limits,
|
||||||
|
emails_per_session: limits,
|
||||||
|
attempt_per_session: limits,
|
||||||
|
},
|
||||||
|
account_recovery: {
|
||||||
|
per_ip: limits,
|
||||||
|
per_address: limits,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
.start();
|
.start();
|
||||||
|
|
||||||
homeserver.withConfig({
|
homeserver.withConfig({
|
||||||
@@ -40,16 +51,10 @@ export const masHomeserver: Fixtures = {
|
|||||||
enable_registration_without_verification: undefined,
|
enable_registration_without_verification: undefined,
|
||||||
disable_msisdn_registration: undefined,
|
disable_msisdn_registration: undefined,
|
||||||
password_config: undefined,
|
password_config: undefined,
|
||||||
experimental_features: {
|
matrix_authentication_service: {
|
||||||
msc3861: {
|
enabled: true,
|
||||||
enabled: true,
|
endpoint: "http://mas:8080/",
|
||||||
issuer: `http://mas:8080/`,
|
secret,
|
||||||
introspection_endpoint: "http://mas:8080/oauth2/introspect",
|
|
||||||
client_id: config.clients[0].client_id,
|
|
||||||
client_auth_method: config.clients[0].client_auth_method,
|
|
||||||
client_secret: config.clients[0].client_secret,
|
|
||||||
admin_token: config.matrix.secret,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -59,28 +64,6 @@ export const masHomeserver: Fixtures = {
|
|||||||
{ scope: "worker" },
|
{ scope: "worker" },
|
||||||
],
|
],
|
||||||
|
|
||||||
config: async ({ homeserver, context, mas }, use) => {
|
|
||||||
const issuer = `${mas.baseUrl}/`;
|
|
||||||
const wellKnown = {
|
|
||||||
"m.homeserver": {
|
|
||||||
base_url: homeserver.baseUrl,
|
|
||||||
},
|
|
||||||
"org.matrix.msc2965.authentication": {
|
|
||||||
issuer,
|
|
||||||
account: `${issuer}account`,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Ensure org.matrix.msc2965.authentication is in well-known
|
|
||||||
await context.route("https://localhost/.well-known/matrix/client", async (route) => {
|
|
||||||
await route.fulfill({ json: wellKnown });
|
|
||||||
});
|
|
||||||
|
|
||||||
await use({
|
|
||||||
default_server_config: wellKnown,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
context: async ({ homeserverType, context }, use, testInfo) => {
|
context: async ({ homeserverType, context }, use, testInfo) => {
|
||||||
testInfo.skip(homeserverType !== "synapse", "does not yet support MAS");
|
testInfo.skip(homeserverType !== "synapse", "does not yet support MAS");
|
||||||
await use(context);
|
await use(context);
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 268 B |
|
After Width: | Height: | Size: 406 B |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 89 KiB After Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 19 KiB |
24
playwright/testcontainers/mas.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2025 New Vector Ltd.
|
||||||
|
|
||||||
|
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 {
|
||||||
|
MatrixAuthenticationServiceContainer as BaseMatrixAuthenticationServiceContainer,
|
||||||
|
type StartedPostgreSqlContainer,
|
||||||
|
} from "@element-hq/element-web-playwright-common/lib/testcontainers";
|
||||||
|
|
||||||
|
const TAG = "main@sha256:430b1f00e74c3f89f078670f676b4333f6bbe5a339962344b3ae84e99e9bcd7f";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MatrixAuthenticationServiceContainer which freezes the docker digest to
|
||||||
|
* stabilise tests, updated periodically by the `playwright-image-updates.yaml`
|
||||||
|
* workflow.
|
||||||
|
*/
|
||||||
|
export class MatrixAuthenticationServiceContainer extends BaseMatrixAuthenticationServiceContainer {
|
||||||
|
public constructor(db: StartedPostgreSqlContainer) {
|
||||||
|
super(db, `ghcr.io/element-hq/matrix-authentication-service:${TAG}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
|
|
||||||
import { SynapseContainer as BaseSynapseContainer } from "@element-hq/element-web-playwright-common/lib/testcontainers";
|
import { SynapseContainer as BaseSynapseContainer } from "@element-hq/element-web-playwright-common/lib/testcontainers";
|
||||||
|
|
||||||
const TAG = "develop@sha256:a0d8db97e39321166959cecdc4c684daff36fefdc7968de47be7e0fdb1cb4541";
|
const TAG = "develop@sha256:8ce4c1a466e1e32bcffde390250a6785527d235436ae42afa9bf94d2a9288746";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SynapseContainer which freezes the docker digest to stabilise tests,
|
* SynapseContainer which freezes the docker digest to stabilise tests,
|
||||||
|
|||||||
@@ -142,6 +142,7 @@
|
|||||||
@import "./views/dialogs/_GenericFeatureFeedbackDialog.pcss";
|
@import "./views/dialogs/_GenericFeatureFeedbackDialog.pcss";
|
||||||
@import "./views/dialogs/_IncomingSasDialog.pcss";
|
@import "./views/dialogs/_IncomingSasDialog.pcss";
|
||||||
@import "./views/dialogs/_InviteDialog.pcss";
|
@import "./views/dialogs/_InviteDialog.pcss";
|
||||||
|
@import "./views/dialogs/_InviteProgressBody.pcss";
|
||||||
@import "./views/dialogs/_JoinRuleDropdown.pcss";
|
@import "./views/dialogs/_JoinRuleDropdown.pcss";
|
||||||
@import "./views/dialogs/_LeaveSpaceDialog.pcss";
|
@import "./views/dialogs/_LeaveSpaceDialog.pcss";
|
||||||
@import "./views/dialogs/_LocationViewDialog.pcss";
|
@import "./views/dialogs/_LocationViewDialog.pcss";
|
||||||
|
|||||||
@@ -12,31 +12,39 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
* These are defined in `rem` so that they scale with the `font-size` of the root element (which is adjustable via the
|
* These are defined in `rem` so that they scale with the `font-size` of the root element (which is adjustable via the
|
||||||
* "Font size" setting). They exist to make the job of converting designs (which tend to be based in pixels) into CSS
|
* "Font size" setting). They exist to make the job of converting designs (which tend to be based in pixels) into CSS
|
||||||
* easier.
|
* easier.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These variables are now *deprecated* and should not be used in new code; instead Compound typographic tokens
|
||||||
|
* should be used. Direct equivalents for these old font size tokens are listed below; where no equivalent exists,
|
||||||
|
* that suggests that the design is using a non-standard font size and should be updated.
|
||||||
*
|
*
|
||||||
|
* In fact, modern Figma designs should actually use a named Typography style such as "Web/font/heading/sm/semibold",
|
||||||
|
* translates directly to `font: var(--cpd-font-heading-sm-semibold)`.
|
||||||
*/
|
*/
|
||||||
$font-1px: 0.0625rem;
|
$font-1px: 0.0625rem;
|
||||||
$font-8px: 0.5rem;
|
$font-8px: 0.5rem;
|
||||||
$font-9px: 0.5625rem;
|
$font-9px: 0.5625rem;
|
||||||
$font-10px: 0.625rem;
|
$font-10px: 0.625rem;
|
||||||
$font-10-4px: 0.6275rem;
|
$font-10-4px: 0.6275rem;
|
||||||
$font-11px: 0.6875rem;
|
$font-11px: 0.6875rem; /* Compound equivalent: --cpd-font-size-body-xs */
|
||||||
$font-12px: 0.75rem;
|
$font-12px: 0.75rem;
|
||||||
$font-13px: 0.8125rem;
|
$font-13px: 0.8125rem; /* Compound equivalent: --cpd-font-size-body-sm */
|
||||||
$font-14px: 0.875rem;
|
$font-14px: 0.875rem;
|
||||||
$font-15px: 0.9375rem;
|
$font-15px: 0.9375rem; /* Compound equivalent: --cpd-font-size-body-md */
|
||||||
$font-16px: 1rem;
|
$font-16px: 1rem;
|
||||||
$font-17px: 1.0625rem;
|
$font-17px: 1.0625rem; /* Compound equivalent: --cpd-font-size-body-lg */
|
||||||
$font-18px: 1.125rem;
|
$font-18px: 1.125rem;
|
||||||
$font-20px: 1.25rem;
|
$font-20px: 1.25rem; /* Compound equivalent: --cpd-font-size-heading-sm */
|
||||||
$font-22px: 1.375rem;
|
$font-22px: 1.375rem;
|
||||||
$font-23px: 1.4375rem;
|
$font-23px: 1.4375rem;
|
||||||
$font-24px: 1.5rem;
|
$font-24px: 1.5rem; /* Compound equivalent: --cpd-font-size-heading-md */
|
||||||
$font-25px: 1.5625rem;
|
$font-25px: 1.5625rem;
|
||||||
$font-26px: 1.625rem;
|
$font-26px: 1.625rem;
|
||||||
$font-28px: 1.75rem;
|
$font-28px: 1.75rem; /* Compound equivalent: --cpd-font-size-heading-lg */
|
||||||
$font-29px: 1.8125rem;
|
$font-29px: 1.8125rem;
|
||||||
$font-30px: 1.875rem;
|
$font-30px: 1.875rem;
|
||||||
$font-32px: 2rem;
|
$font-32px: 2rem; /* Compound equivalent: --cpd-font-size-heading-xl */
|
||||||
$font-34px: 2.125rem;
|
$font-34px: 2.125rem;
|
||||||
$font-35px: 2.1875rem;
|
$font-35px: 2.1875rem;
|
||||||
$font-39px: 2.4375rem;
|
$font-39px: 2.4375rem;
|
||||||
|
|||||||