Compare commits
2 Commits
t3chguy/dy
...
hs/better-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dac03d0af2 | ||
|
|
4196ea2018 |
@@ -30,10 +30,6 @@ module.exports = {
|
||||
["window.innerHeight", "window.innerWidth", "window.visualViewport"],
|
||||
"Use UIStore to access window dimensions instead.",
|
||||
),
|
||||
...buildRestrictedPropertiesOptions(
|
||||
["React.forwardRef", "*.forwardRef", "forwardRef"],
|
||||
"Use ref props instead.",
|
||||
),
|
||||
...buildRestrictedPropertiesOptions(
|
||||
["*.mxcUrlToHttp", "*.getHttpUriForMxc"],
|
||||
"Use Media helper instead to centralise access for customisation.",
|
||||
@@ -59,11 +55,6 @@ module.exports = {
|
||||
"error",
|
||||
{
|
||||
paths: [
|
||||
{
|
||||
name: "react",
|
||||
importNames: ["forwardRef"],
|
||||
message: "Use ref props instead.",
|
||||
},
|
||||
{
|
||||
name: "@testing-library/react",
|
||||
message: "Please use jest-matrix-react instead",
|
||||
|
||||
6
.github/workflows/build.yml
vendored
@@ -43,9 +43,9 @@ jobs:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
# Disable cache on Windows as it is slower than not caching
|
||||
# https://github.com/actions/setup-node/issues/975
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
yarn build
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: webapp-${{ matrix.image }}
|
||||
path: webapp
|
||||
|
||||
4
.github/workflows/build_debian.yaml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
R2_URL: ${{ vars.CF_R2_S3_API }}
|
||||
VERSION: ${{ github.ref_name }}
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Download package
|
||||
run: |
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
dpkg-gencontrol -v"$VERSION" -ldebian/tmp/DEBIAN/changelog
|
||||
dpkg-deb -Zxz --root-owner-group --build debian/tmp element-web.deb
|
||||
|
||||
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: element-web.deb
|
||||
path: element-web.deb
|
||||
|
||||
6
.github/workflows/build_develop.yml
vendored
@@ -26,9 +26,9 @@ jobs:
|
||||
R2_URL: ${{ vars.CF_R2_S3_API }}
|
||||
R2_PUBLIC_URL: "https://element-web-develop.element.io"
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
cache: "yarn"
|
||||
node-version: "lts/*"
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
|
||||
- run: mv dist/element-*.tar.gz dist/develop.tar.gz
|
||||
|
||||
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: webapp
|
||||
path: dist/develop.tar.gz
|
||||
|
||||
2
.github/workflows/deploy.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
||||
env:
|
||||
SITE: ${{ inputs.site || 'staging.element.io' }}
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Load GPG key
|
||||
run: |
|
||||
|
||||
4
.github/workflows/docker.yaml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
env:
|
||||
TEST_TAG: vectorim/element-web:test
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # needed for docker-package to be able to calculate the version
|
||||
|
||||
@@ -132,7 +132,7 @@ jobs:
|
||||
cosign sign --yes ${images}
|
||||
|
||||
- name: Update repo description
|
||||
uses: peter-evans/dockerhub-description@432a30c9e07499fd01da9f8a49f0faf9e0ca5b77 # v4
|
||||
uses: peter-evans/dockerhub-description@0505d8b04853a30189aee66f5bb7fd1511bbac71 # v4
|
||||
if: github.event_name != 'pull_request'
|
||||
continue-on-error: true
|
||||
with:
|
||||
|
||||
14
.github/workflows/docs.yml
vendored
@@ -17,23 +17,23 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Fetch element-desktop
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: element-hq/element-desktop
|
||||
path: element-desktop
|
||||
|
||||
- name: Fetch element-web
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: element-web
|
||||
|
||||
- name: Fetch matrix-js-sdk
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: matrix-org/matrix-js-sdk
|
||||
path: matrix-js-sdk
|
||||
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
cache: "yarn"
|
||||
cache-dependency-path: element-web/yarn.lock
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
echo "- [Automations](automations.md)" >> docs/SUMMARY.md
|
||||
|
||||
- name: Setup mdBook
|
||||
uses: peaceiris/actions-mdbook@ee69d230fe19748b7abf22df32acaa93833fad08 # v2
|
||||
uses: peaceiris/actions-mdbook@v2
|
||||
with:
|
||||
mdbook-version: "0.4.10"
|
||||
|
||||
@@ -88,7 +88,7 @@ jobs:
|
||||
run: mdbook build
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: ./book
|
||||
|
||||
@@ -104,4 +104,4 @@ jobs:
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4
|
||||
uses: actions/deploy-pages@v4
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
actions: read
|
||||
steps:
|
||||
- name: Download HTML report
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
path: playwright-report
|
||||
|
||||
- name: 📤 Deploy to Netlify
|
||||
uses: matrix-org/netlify-pr-preview@9805cd123fc9a7e421e35340a05e1ebc5dee46b5 # v3
|
||||
uses: matrix-org/netlify-pr-preview@v3
|
||||
with:
|
||||
path: playwright-report
|
||||
owner: ${{ github.event.workflow_run.head_repository.owner.login }}
|
||||
|
||||
26
.github/workflows/end-to-end-tests.yaml
vendored
@@ -50,11 +50,11 @@ jobs:
|
||||
runners-matrix: ${{ steps.runner-vars.outputs.matrix }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: element-hq/element-web
|
||||
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
cache: "yarn"
|
||||
node-version: "lts/*"
|
||||
@@ -81,7 +81,7 @@ jobs:
|
||||
yarn build
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: webapp
|
||||
path: webapp
|
||||
@@ -89,7 +89,7 @@ jobs:
|
||||
|
||||
- name: Calculate runner variables
|
||||
id: runner-vars
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const numRunners = parseInt(process.env.NUM_RUNNERS, 10);
|
||||
@@ -129,18 +129,18 @@ jobs:
|
||||
- runAllTests: false
|
||||
project: Pinecone
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
repository: element-hq/element-web
|
||||
|
||||
- name: 📥 Download artifact
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: webapp
|
||||
path: webapp
|
||||
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
cache: "yarn"
|
||||
cache-dependency-path: yarn.lock
|
||||
@@ -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
|
||||
|
||||
- name: Cache playwright binaries
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4
|
||||
uses: actions/cache@v4
|
||||
id: playwright-cache
|
||||
with:
|
||||
path: |
|
||||
@@ -180,7 +180,7 @@ jobs:
|
||||
|
||||
- name: Upload blob report to GitHub Actions Artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: all-blob-reports-${{ matrix.project }}-${{ matrix.runner }}
|
||||
path: blob-report
|
||||
@@ -192,13 +192,13 @@ jobs:
|
||||
if: always()
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: actions/checkout@v4
|
||||
if: inputs.skip != true
|
||||
with:
|
||||
persist-credentials: false
|
||||
repository: element-hq/element-web
|
||||
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
- uses: actions/setup-node@v4
|
||||
if: inputs.skip != true
|
||||
with:
|
||||
cache: "yarn"
|
||||
@@ -210,7 +210,7 @@ jobs:
|
||||
|
||||
- name: Download blob reports from GitHub Actions Artifacts
|
||||
if: inputs.skip != true
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: all-blob-reports-*
|
||||
path: all-blob-reports
|
||||
@@ -226,7 +226,7 @@ jobs:
|
||||
# Upload the HTML report even if one of our reporters fails, this can happen when stale screenshots are detected
|
||||
- name: Upload HTML report
|
||||
if: always() && inputs.skip != true
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: html-report
|
||||
path: playwright-report
|
||||
|
||||
4
.github/workflows/issue_closed.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
name: Tidy closed issues
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
||||
- uses: actions/github-script@v7
|
||||
id: main
|
||||
with:
|
||||
# PAT needed as the GITHUB_TOKEN won't be able to see cross-references from other orgs (matrix-org)
|
||||
@@ -142,7 +142,7 @@ jobs:
|
||||
});
|
||||
}
|
||||
}
|
||||
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
||||
- uses: actions/github-script@v7
|
||||
name: Close duplicate as Not Planned
|
||||
if: steps.main.outputs.closeAsNotPlanned
|
||||
with:
|
||||
|
||||
4
.github/workflows/netlify.yaml
vendored
@@ -28,7 +28,7 @@ jobs:
|
||||
Exercise caution. Use test accounts.
|
||||
|
||||
- name: 📥 Download artifact
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
path: webapp
|
||||
|
||||
- name: 📤 Deploy to Netlify
|
||||
uses: matrix-org/netlify-pr-preview@9805cd123fc9a7e421e35340a05e1ebc5dee46b5 # v3
|
||||
uses: matrix-org/netlify-pr-preview@v3
|
||||
with:
|
||||
path: webapp
|
||||
owner: ${{ github.event.workflow_run.head_repository.owner.login }}
|
||||
|
||||
2
.github/workflows/pending-reviews.yaml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
URL: "https://github.com/pulls?q=is%3Apr+is%3Aopen+repo%3Amatrix-org%2Fmatrix-js-sdk+repo%3Amatrix-org%2Fmatrix-react-sdk+repo%3Aelement-hq%2Felement-web+repo%3Aelement-hq%2Felement-desktop+review-requested%3A%40me+sort%3Aupdated-desc+"
|
||||
RELEASE_BLOCKERS_URL: "https://github.com/pulls?q=is%3Aopen+repo%3Amatrix-org%2Fmatrix-js-sdk+repo%3Amatrix-org%2Fmatrix-react-sdk+repo%3Aelement-hq%2Felement-web+repo%3Aelement-hq%2Felement-desktop+sort%3Aupdated-desc+label%3AX-Release-Blocker+"
|
||||
steps:
|
||||
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
||||
- uses: actions/github-script@v7
|
||||
env:
|
||||
HS_URL: ${{ secrets.BETABOT_HS_URL }}
|
||||
ROOM_ID: ${{ secrets.ROOM_ID }}
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Update synapse image
|
||||
run: |
|
||||
|
||||
@@ -8,7 +8,7 @@ jobs:
|
||||
name: Check PR base branch
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
||||
- uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const baseBranch = context.payload.pull_request.base.ref;
|
||||
|
||||
12
.github/workflows/release_prepare.yml
vendored
@@ -41,7 +41,7 @@ jobs:
|
||||
REPOS: matrix-js-sdk element-web element-desktop
|
||||
steps:
|
||||
- name: Checkout Element Desktop
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@v4
|
||||
if: inputs.element-desktop
|
||||
with:
|
||||
repository: element-hq/element-desktop
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
fetch-tags: true
|
||||
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
- name: Checkout Element Web
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@v4
|
||||
if: inputs.element-web
|
||||
with:
|
||||
repository: element-hq/element-web
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
fetch-tags: true
|
||||
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
- name: Checkout Matrix JS SDK
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@v4
|
||||
if: inputs.matrix-js-sdk
|
||||
with:
|
||||
repository: matrix-org/matrix-js-sdk
|
||||
@@ -100,7 +100,7 @@ jobs:
|
||||
repo: matrix-org/matrix-js-sdk
|
||||
repo-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
wait-interval: 10
|
||||
check-name: "draft / draft"
|
||||
check-name: draft
|
||||
allowed-conclusions: success
|
||||
|
||||
- name: Wait for element-web draft
|
||||
@@ -111,7 +111,7 @@ jobs:
|
||||
repo: element-hq/element-web
|
||||
repo-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
wait-interval: 10
|
||||
check-name: "draft / draft"
|
||||
check-name: draft
|
||||
allowed-conclusions: success
|
||||
|
||||
- name: Wait for element-desktop draft
|
||||
@@ -122,5 +122,5 @@ jobs:
|
||||
repo: element-hq/element-desktop
|
||||
repo-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
wait-interval: 10
|
||||
check-name: "draft / draft"
|
||||
check-name: draft
|
||||
allowed-conclusions: success
|
||||
|
||||
22
.github/workflows/static_analysis.yaml
vendored
@@ -23,9 +23,9 @@ jobs:
|
||||
name: "Typescript Syntax Check"
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
cache: "yarn"
|
||||
node-version: "lts/*"
|
||||
@@ -57,7 +57,7 @@ jobs:
|
||||
name: "Rethemendex Check"
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- run: ./res/css/rethemendex.sh
|
||||
|
||||
@@ -67,9 +67,9 @@ jobs:
|
||||
name: "ESLint"
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
cache: "yarn"
|
||||
node-version: "lts/*"
|
||||
@@ -85,9 +85,9 @@ jobs:
|
||||
name: "Style Lint"
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
cache: "yarn"
|
||||
node-version: "lts/*"
|
||||
@@ -103,9 +103,9 @@ jobs:
|
||||
name: "Workflow Lint"
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
cache: "yarn"
|
||||
node-version: "lts/*"
|
||||
@@ -121,9 +121,9 @@ jobs:
|
||||
name: "Analyse Dead Code"
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
cache: "yarn"
|
||||
node-version: "lts/*"
|
||||
|
||||
10
.github/workflows/tests.yml
vendored
@@ -39,12 +39,12 @@ jobs:
|
||||
runner: [1, 2]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ inputs.matrix-js-sdk-sha && 'element-hq/element-web' || github.repository }}
|
||||
|
||||
- name: Yarn cache
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "lts/*"
|
||||
cache: "yarn"
|
||||
@@ -55,7 +55,7 @@ jobs:
|
||||
JS_SDK_GITHUB_BASE_REF: ${{ inputs.matrix-js-sdk-sha }}
|
||||
|
||||
- name: Jest Cache
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: /tmp/jest_cache
|
||||
key: ${{ hashFiles('**/yarn.lock') }}
|
||||
@@ -84,7 +84,7 @@ jobs:
|
||||
|
||||
- name: Upload Artifact
|
||||
if: env.ENABLE_COVERAGE == 'true'
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: coverage-${{ matrix.runner }}
|
||||
path: |
|
||||
@@ -104,7 +104,7 @@ jobs:
|
||||
|
||||
- name: Skip SonarCloud in merge queue
|
||||
if: github.event_name == 'merge_group' || inputs.disable_coverage == 'true'
|
||||
uses: guibranco/github-status-action-v2@5f2b01ce1394109f70954ae6b69ef41cf7928e63
|
||||
uses: guibranco/github-status-action-v2@9b1d102b3c32583174557f58c53e3b09d43d1b1d
|
||||
with:
|
||||
authToken: ${{ secrets.GITHUB_TOKEN }}
|
||||
state: success
|
||||
|
||||
3
.github/workflows/triage-assigned.yml
vendored
@@ -11,8 +11,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
if: |
|
||||
contains(github.event.issue.assignees.*.login, 't3chguy') ||
|
||||
contains(github.event.issue.assignees.*.login, 'florianduros') ||
|
||||
contains(github.event.issue.assignees.*.login, 'dbkr') ||
|
||||
contains(github.event.issue.assignees.*.login, 'andybalaam') ||
|
||||
contains(github.event.issue.assignees.*.login, 'MidhunSureshR')
|
||||
steps:
|
||||
- uses: actions/add-to-project@main
|
||||
|
||||
16
.github/workflows/triage-labelled.yml
vendored
@@ -27,7 +27,7 @@ jobs:
|
||||
contains(github.event.issue.labels.*.name, 'A-Rich-Text-Editor') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Element-Call')
|
||||
steps:
|
||||
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
||||
- uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
github.rest.issues.addLabels({
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
contains(github.event.issue.labels.*.name, 'good first issue') ||
|
||||
contains(github.event.issue.labels.*.name, 'Hacktoberfest')
|
||||
steps:
|
||||
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
||||
- uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
github.rest.issues.addLabels({
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
contains(github.event.issue.labels.*.name, 'X-Needs-Info')
|
||||
steps:
|
||||
- id: add_to_project
|
||||
uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2
|
||||
uses: actions/add-to-project@v1.0.2
|
||||
with:
|
||||
project-url: ${{ env.PROJECT_URL }}
|
||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
@@ -84,7 +84,7 @@ jobs:
|
||||
contains(github.event.issue.labels.*.name, 'Z-Flaky-Test')
|
||||
steps:
|
||||
- id: add_to_project
|
||||
uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2
|
||||
uses: actions/add-to-project@v1.0.2
|
||||
with:
|
||||
project-url: ${{ env.PROJECT_URL }}
|
||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
@@ -150,15 +150,15 @@ jobs:
|
||||
project-url: https://github.com/orgs/element-hq/projects/41
|
||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
crypto:
|
||||
name: Add labelled issues to Crypto project
|
||||
verticals_feature:
|
||||
name: Add labelled issues to Verticals Feature project
|
||||
runs-on: ubuntu-24.04
|
||||
if: >
|
||||
contains(github.event.issue.labels.*.name, 'Team: Crypto')
|
||||
contains(github.event.issue.labels.*.name, 'Team: Verticals Feature')
|
||||
steps:
|
||||
- uses: actions/add-to-project@main
|
||||
with:
|
||||
project-url: https://github.com/orgs/element-hq/projects/76
|
||||
project-url: https://github.com/orgs/element-hq/projects/57
|
||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
tech_debt:
|
||||
|
||||
2
.github/workflows/triage-stale.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
operations-per-run: 100
|
||||
# Flaky test issue closing
|
||||
|
||||
2
.github/workflows/triage-unlabelled.yml
vendored
@@ -62,7 +62,7 @@ jobs:
|
||||
contains(github.event.issue.labels.*.name, 'A-Element-Call')) &&
|
||||
contains(github.event.issue.labels.*.name, 'Z-Labs')
|
||||
steps:
|
||||
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
||||
- uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
github.rest.issues.removeLabel({
|
||||
|
||||
4
.github/workflows/update-jitsi.yml
vendored
@@ -9,9 +9,9 @@ jobs:
|
||||
update:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
cache: "yarn"
|
||||
node-version: "lts/*"
|
||||
|
||||
2
.github/workflows/update-topics.yaml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
environment: Matrix
|
||||
steps:
|
||||
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
||||
- uses: actions/github-script@v7
|
||||
env:
|
||||
HS_URL: ${{ secrets.BETABOT_HS_URL }}
|
||||
LOBBY_ROOM_ID: ${{ secrets.ROOM_ID }}
|
||||
|
||||
1
.gitignore
vendored
@@ -25,6 +25,7 @@ electron/pub
|
||||
.env
|
||||
/coverage
|
||||
# Auto-generated file
|
||||
/src/modules.ts
|
||||
/src/modules.js
|
||||
/build_config.yaml
|
||||
/book
|
||||
|
||||
59
CHANGELOG.md
@@ -1,62 +1,3 @@
|
||||
Changes in [1.11.100](https://github.com/element-hq/element-web/releases/tag/v1.11.100) (2025-05-06)
|
||||
====================================================================================================
|
||||
## ✨ Features
|
||||
|
||||
* Move rich topics out of labs / stabilise MSC3765 ([#29817](https://github.com/element-hq/element-web/pull/29817)). Contributed by @Johennes.
|
||||
* Spell out that Element Web does \*not\* work on mobile. ([#29211](https://github.com/element-hq/element-web/pull/29211)). Contributed by @ara4n.
|
||||
* Add message preview support to the new room list ([#29784](https://github.com/element-hq/element-web/pull/29784)). Contributed by @dbkr.
|
||||
* Global configuration flag for media previews ([#29582](https://github.com/element-hq/element-web/pull/29582)). Contributed by @Half-Shot.
|
||||
* New room list: add partial keyboard shortcuts support ([#29783](https://github.com/element-hq/element-web/pull/29783)). Contributed by @florianduros.
|
||||
* MVVM RoomSummaryCard Topic ([#29710](https://github.com/element-hq/element-web/pull/29710)). Contributed by @MarcWadai.
|
||||
* Warn on self change from settings > roles ([#28926](https://github.com/element-hq/element-web/pull/28926)). Contributed by @MarcWadai.
|
||||
* New room list: new visual for invitation ([#29773](https://github.com/element-hq/element-web/pull/29773)). Contributed by @florianduros.
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
|
||||
* Fix incorrect display of the user info display name ([#29826](https://github.com/element-hq/element-web/pull/29826)). Contributed by @langleyd.
|
||||
* RoomListStore: Remove invite rooms on decline ([#29804](https://github.com/element-hq/element-web/pull/29804)). Contributed by @MidhunSureshR.
|
||||
* Fix the buttons not being displayed with long preview text ([#29811](https://github.com/element-hq/element-web/pull/29811)). Contributed by @dbkr.
|
||||
* New room list: fix missing/incorrect notification decoration ([#29796](https://github.com/element-hq/element-web/pull/29796)). Contributed by @florianduros.
|
||||
* New Room List: Prevent potential scroll jump/flicker when switching spaces ([#29781](https://github.com/element-hq/element-web/pull/29781)). Contributed by @MidhunSureshR.
|
||||
* New room list: fix incorrect decoration ([#29770](https://github.com/element-hq/element-web/pull/29770)). Contributed by @florianduros.
|
||||
|
||||
|
||||
Changes in [1.11.99](https://github.com/element-hq/element-web/releases/tag/v1.11.99) (2025-04-23)
|
||||
==================================================================================================
|
||||
No changes, just bumping the version to accommodate a new Element Desktop release
|
||||
|
||||
Changes in [1.11.98](https://github.com/element-hq/element-web/releases/tag/v1.11.98) (2025-04-22)
|
||||
==================================================================================================
|
||||
## ✨ Features
|
||||
|
||||
* print better errors in the search view instead of a blocking modal ([#29724](https://github.com/element-hq/element-web/pull/29724)). Contributed by @Jujure.
|
||||
* New room list: video room and video call decoration ([#29693](https://github.com/element-hq/element-web/pull/29693)). Contributed by @florianduros.
|
||||
* Remove Secure Backup, Cross-signing and Cryptography sections in `Security & Privacy` user settings ([#29088](https://github.com/element-hq/element-web/pull/29088)). Contributed by @florianduros.
|
||||
* Allow reporting a room when rejecting an invite. ([#29570](https://github.com/element-hq/element-web/pull/29570)). Contributed by @Half-Shot.
|
||||
* RoomListViewModel: Reset primary and secondary filters on space change ([#29672](https://github.com/element-hq/element-web/pull/29672)). Contributed by @MidhunSureshR.
|
||||
* RoomListStore: Support specific sorting requirements for muted rooms ([#29665](https://github.com/element-hq/element-web/pull/29665)). Contributed by @MidhunSureshR.
|
||||
* New room list: add notification options menu ([#29639](https://github.com/element-hq/element-web/pull/29639)). Contributed by @florianduros.
|
||||
* Room List: Scroll to top of the list when active room is not in the list ([#29650](https://github.com/element-hq/element-web/pull/29650)). Contributed by @MidhunSureshR.
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
|
||||
* Fix unwanted form submit behaviour in memberlist ([#29747](https://github.com/element-hq/element-web/pull/29747)). Contributed by @MidhunSureshR.
|
||||
* New room list: fix public room icon visibility when filter change ([#29737](https://github.com/element-hq/element-web/pull/29737)). Contributed by @florianduros.
|
||||
* Fix custom theme support for short hex \& rgba hex strings ([#29726](https://github.com/element-hq/element-web/pull/29726)). Contributed by @t3chguy.
|
||||
* New room list: minor visual fixes ([#29723](https://github.com/element-hq/element-web/pull/29723)). Contributed by @florianduros.
|
||||
* Fix getOidcCallbackUrl for Element Desktop ([#29711](https://github.com/element-hq/element-web/pull/29711)). Contributed by @t3chguy.
|
||||
* Fix some webp images improperly marked as animated ([#29713](https://github.com/element-hq/element-web/pull/29713)). Contributed by @Petersmit27.
|
||||
* Revert deletion of hydrateSession ([#29703](https://github.com/element-hq/element-web/pull/29703)). Contributed by @Jujure.
|
||||
* Fix converttoroom \& converttodm not working ([#29705](https://github.com/element-hq/element-web/pull/29705)). Contributed by @t3chguy.
|
||||
* Ensure forceCloseAllModals also closes priority/static modals ([#29706](https://github.com/element-hq/element-web/pull/29706)). Contributed by @t3chguy.
|
||||
* Continue button is disabled when uploading a recovery key file ([#29695](https://github.com/element-hq/element-web/pull/29695)). Contributed by @Giwayume.
|
||||
* Catch errors after syncing recovery ([#29691](https://github.com/element-hq/element-web/pull/29691)). Contributed by @andybalaam.
|
||||
* New room list: fix multiple visual issues ([#29673](https://github.com/element-hq/element-web/pull/29673)). Contributed by @florianduros.
|
||||
* New Room List: Fix mentions filter matching rooms with any highlight ([#29668](https://github.com/element-hq/element-web/pull/29668)). Contributed by @MidhunSureshR.
|
||||
* Fix truncated emoji label during emoji SAS ([#29643](https://github.com/element-hq/element-web/pull/29643)). Contributed by @florianduros.
|
||||
* Remove duplicate jitsi link ([#29642](https://github.com/element-hq/element-web/pull/29642)). Contributed by @dbkr.
|
||||
|
||||
|
||||
Changes in [1.11.97](https://github.com/element-hq/element-web/releases/tag/v1.11.97) (2025-04-08)
|
||||
==================================================================================================
|
||||
## ✨ Features
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# syntax=docker.io/docker/dockerfile:1.15-labs@sha256:94edd5b349df43675bd6f542e2b9a24e7177432dec45fe3066bfcf2ab14c4355
|
||||
# syntax=docker.io/docker/dockerfile:1.14-labs
|
||||
|
||||
# Builder
|
||||
FROM --platform=$BUILDPLATFORM node:22-bullseye@sha256:ed0338dd02fd86861a59dc1cbc2e12152f3a93c4ce5933d347d6677232000dc7 AS builder
|
||||
FROM --platform=$BUILDPLATFORM node:22-bullseye AS builder
|
||||
|
||||
# Support custom branch of the js-sdk. This also helps us build images of element-web develop.
|
||||
ARG USE_CUSTOM_SDKS=false
|
||||
@@ -19,7 +19,7 @@ RUN /src/scripts/docker-package.sh
|
||||
RUN cp /src/config.sample.json /src/webapp/config.json
|
||||
|
||||
# App
|
||||
FROM nginxinc/nginx-unprivileged:alpine-slim@sha256:0a49675e3e35cc2f89ce831f00f767af9c32df04f5a80167739fd32346f1fe99
|
||||
FROM nginxinc/nginx-unprivileged:alpine-slim
|
||||
|
||||
# Need root user to install packages & manipulate the usr directory
|
||||
USER root
|
||||
|
||||
@@ -126,7 +126,7 @@ guide](https://classic.yarnpkg.com/en/docs/install) if you do not have it alread
|
||||
1. Install the prerequisites: `yarn install`.
|
||||
- If you're using the `develop` branch, then it is recommended to set up a
|
||||
proper development environment (see [Setting up a dev
|
||||
environment](./developer_guide.md#setting-up-a-dev-environment) below). Alternatively, you
|
||||
environment](#setting-up-a-dev-environment) below). Alternatively, you
|
||||
can use <https://develop.element.io> - the continuous integration release of
|
||||
the develop branch.
|
||||
1. Configure the app by copying `config.sample.json` to `config.json` and
|
||||
|
||||
@@ -101,6 +101,10 @@ Under the hood this stops Element Web from adding the `perParticipantE2EE` flag
|
||||
|
||||
This is useful while we experiment with encryption and to make calling compatible with platforms that don't use encryption yet.
|
||||
|
||||
## Rich text in room topics (`feature_html_topic`) [In Development]
|
||||
|
||||
Enables rendering of MD / HTML in room topics.
|
||||
|
||||
## Enable the notifications panel in the room header (`feature_notifications`)
|
||||
|
||||
Unreliable in encrypted rooms.
|
||||
|
||||
56
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "element-web",
|
||||
"version": "1.11.100",
|
||||
"version": "1.11.97",
|
||||
"description": "Element: the future of secure communication",
|
||||
"author": "New Vector Ltd.",
|
||||
"repository": {
|
||||
@@ -68,32 +68,32 @@
|
||||
"postinstall": "patch-package"
|
||||
},
|
||||
"resolutions": {
|
||||
"**/pretty-format/react-is": "19.1.0",
|
||||
"@playwright/test": "1.52.0",
|
||||
"@types/react": "19.1.4",
|
||||
"@types/react-dom": "19.1.5",
|
||||
"oidc-client-ts": "3.2.1",
|
||||
"**/pretty-format/react-is": "19.0.0",
|
||||
"@playwright/test": "1.51.1",
|
||||
"@types/react": "19.0.10",
|
||||
"@types/react-dom": "19.0.4",
|
||||
"oidc-client-ts": "3.2.0",
|
||||
"jwt-decode": "4.0.0",
|
||||
"caniuse-lite": "1.0.30001717",
|
||||
"testcontainers": "10.25.0",
|
||||
"caniuse-lite": "1.0.30001707",
|
||||
"testcontainers": "10.23.0",
|
||||
"wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0",
|
||||
"wrap-ansi": "npm:wrap-ansi@^7.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@element-hq/element-web-module-api": "1.0.0",
|
||||
"@element-hq/element-web-module-api": "^0.1.1",
|
||||
"@fontsource/inconsolata": "^5",
|
||||
"@fontsource/inter": "^5",
|
||||
"@formatjs/intl-segmenter": "^11.5.7",
|
||||
"@matrix-org/analytics-events": "^0.29.2",
|
||||
"@matrix-org/emojibase-bindings": "^1.3.4",
|
||||
"@matrix-org/emojibase-bindings": "^1.4.0",
|
||||
"@matrix-org/react-sdk-module-api": "^2.4.0",
|
||||
"@matrix-org/spec": "^1.7.0",
|
||||
"@sentry/browser": "^9.0.0",
|
||||
"@types/png-chunks-extract": "^1.0.2",
|
||||
"@types/react-virtualized": "^9.21.30",
|
||||
"@vector-im/compound-design-tokens": "^4.0.0",
|
||||
"@vector-im/compound-web": "^7.11.0",
|
||||
"@vector-im/compound-web": "^7.10.1",
|
||||
"@vector-im/matrix-wysiwyg": "2.38.3",
|
||||
"@zxcvbn-ts/core": "^3.0.4",
|
||||
"@zxcvbn-ts/language-common": "^3.0.4",
|
||||
@@ -109,7 +109,7 @@
|
||||
"diff-dom": "^5.0.0",
|
||||
"diff-match-patch": "^1.0.5",
|
||||
"domutils": "^3.2.2",
|
||||
"emojibase-regex": "15.3.2",
|
||||
"emojibase-regex": "16.0.0",
|
||||
"escape-html": "^1.0.3",
|
||||
"file-saver": "^2.0.5",
|
||||
"filesize": "10.1.6",
|
||||
@@ -123,9 +123,9 @@
|
||||
"jsrsasign": "^11.0.0",
|
||||
"jszip": "^3.7.0",
|
||||
"katex": "^0.16.0",
|
||||
"linkify-react": "4.3.1",
|
||||
"linkify-string": "4.3.1",
|
||||
"linkifyjs": "4.3.1",
|
||||
"linkify-react": "4.2.0",
|
||||
"linkify-string": "4.2.0",
|
||||
"linkifyjs": "4.2.0",
|
||||
"lodash": "^4.17.21",
|
||||
"maplibre-gl": "^5.0.0",
|
||||
"matrix-encrypt-attachment": "^1.0.3",
|
||||
@@ -138,7 +138,7 @@
|
||||
"opus-recorder": "^8.0.3",
|
||||
"pako": "^2.0.3",
|
||||
"png-chunks-extract": "^1.0.0",
|
||||
"posthog-js": "1.242.1",
|
||||
"posthog-js": "1.157.2",
|
||||
"qrcode": "1.5.4",
|
||||
"re-resizable": "6.11.2",
|
||||
"react": "^19.0.0",
|
||||
@@ -151,7 +151,7 @@
|
||||
"react-virtualized": "^9.22.5",
|
||||
"rfc4648": "^1.4.0",
|
||||
"sanitize-filename": "^1.6.3",
|
||||
"sanitize-html": "2.16.0",
|
||||
"sanitize-html": "2.15.0",
|
||||
"tar-js": "^0.3.0",
|
||||
"temporal-polyfill": "^0.3.0",
|
||||
"ua-parser-js": "^1.0.2",
|
||||
@@ -180,14 +180,13 @@
|
||||
"@babel/preset-typescript": "^7.12.7",
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@casualbot/jest-sonar-reporter": "2.2.7",
|
||||
"@element-hq/element-call-embedded": "0.10.0",
|
||||
"@element-hq/element-call-embedded": "0.9.0",
|
||||
"@element-hq/element-web-playwright-common": "^1.1.5",
|
||||
"@peculiar/webcrypto": "^1.4.3",
|
||||
"@playwright/test": "^1.50.1",
|
||||
"@principalstudio/html-webpack-inject-preload": "^1.2.7",
|
||||
"@rrweb/types": "^2.0.0-alpha.18",
|
||||
"@sentry/webpack-plugin": "^3.0.0",
|
||||
"@stylistic/eslint-plugin": "^4.0.0",
|
||||
"@stylistic/eslint-plugin": "^3.0.0",
|
||||
"@svgr/webpack": "^8.0.0",
|
||||
"@testing-library/dom": "^10.4.0",
|
||||
"@testing-library/jest-dom": "^6.4.8",
|
||||
@@ -212,11 +211,11 @@
|
||||
"@types/node-fetch": "^2.6.2",
|
||||
"@types/pako": "^2.0.0",
|
||||
"@types/qrcode": "^1.3.5",
|
||||
"@types/react": "19.1.4",
|
||||
"@types/react": "19.0.10",
|
||||
"@types/react-beautiful-dnd": "^13.0.0",
|
||||
"@types/react-dom": "19.1.5",
|
||||
"@types/react-dom": "19.0.4",
|
||||
"@types/react-transition-group": "^4.4.0",
|
||||
"@types/sanitize-html": "2.16.0",
|
||||
"@types/sanitize-html": "2.15.0",
|
||||
"@types/semver": "^7.5.8",
|
||||
"@types/tar-js": "^0.3.5",
|
||||
"@types/ua-parser-js": "^0.7.36",
|
||||
@@ -247,7 +246,7 @@
|
||||
"eslint-plugin-react-compiler": "^19.0.0-beta-df7b47d-20241124",
|
||||
"eslint-plugin-react-hooks": "^5.0.0",
|
||||
"eslint-plugin-unicorn": "^56.0.0",
|
||||
"express": "^5.0.0",
|
||||
"express": "^4.18.2",
|
||||
"fake-indexeddb": "^6.0.0",
|
||||
"fetch-mock": "9.11.0",
|
||||
"fetch-mock-jest": "^1.5.1",
|
||||
@@ -262,7 +261,7 @@
|
||||
"jest-raw-loader": "^1.0.1",
|
||||
"jsqr": "^1.4.0",
|
||||
"knip": "^5.36.2",
|
||||
"lint-staged": "^16.0.0",
|
||||
"lint-staged": "^15.0.2",
|
||||
"matrix-web-i18n": "^3.2.1",
|
||||
"mini-css-extract-plugin": "2.9.2",
|
||||
"minimist": "^1.2.6",
|
||||
@@ -287,13 +286,13 @@
|
||||
"semver": "^7.5.2",
|
||||
"source-map-loader": "^5.0.0",
|
||||
"stylelint": "^16.13.0",
|
||||
"stylelint-config-standard": "^38.0.0",
|
||||
"stylelint-config-standard": "^37.0.0",
|
||||
"stylelint-scss": "^6.0.0",
|
||||
"stylelint-value-no-unknown-custom-properties": "^6.0.1",
|
||||
"terser-webpack-plugin": "^5.3.9",
|
||||
"testcontainers": "^10.20.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "5.8.3",
|
||||
"typescript": "5.8.2",
|
||||
"util": "^0.12.5",
|
||||
"web-streams-polyfill": "^4.0.0",
|
||||
"webpack": "^5.89.0",
|
||||
@@ -311,6 +310,5 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/node_modules/@types/react/index.d.ts b/node_modules/@types/react/index.d.ts
|
||||
index d3318dc..c2b2c77 100644
|
||||
index 2272032..18bd20a 100644
|
||||
--- a/node_modules/@types/react/index.d.ts
|
||||
+++ b/node_modules/@types/react/index.d.ts
|
||||
@@ -134,7 +134,7 @@ declare namespace React {
|
||||
@@ -11,7 +11,7 @@ index d3318dc..c2b2c77 100644
|
||||
|
||||
/**
|
||||
* Created by {@link createRef}, or {@link useRef} when passed `null`.
|
||||
@@ -945,7 +945,7 @@ declare namespace React {
|
||||
@@ -941,7 +941,7 @@ declare namespace React {
|
||||
context: unknown;
|
||||
|
||||
// Keep in sync with constructor signature of JSXElementConstructor and ComponentClass.
|
||||
@@ -20,7 +20,7 @@ index d3318dc..c2b2c77 100644
|
||||
|
||||
// We MUST keep setState() as a unified signature because it allows proper checking of the method return type.
|
||||
// See: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18365#issuecomment-351013257
|
||||
@@ -1117,7 +1117,7 @@ declare namespace React {
|
||||
@@ -1113,7 +1113,7 @@ declare namespace React {
|
||||
*/
|
||||
interface ComponentClass<P = {}, S = ComponentState> extends StaticLifecycle<P, S> {
|
||||
// constructor signature must match React.Component
|
||||
@@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
import { test, expect } from "../../element-web-test";
|
||||
import { isDendrite } from "../../plugins/homeserver/dendrite";
|
||||
import { createBot, logIntoElement } from "./utils.ts";
|
||||
import { completeCreateSecretStorageDialog, createBot, logIntoElement } from "./utils.ts";
|
||||
import { type Client } from "../../pages/client.ts";
|
||||
import { type ElementAppPage } from "../../pages/ElementAppPage.ts";
|
||||
|
||||
@@ -28,27 +28,21 @@ test.describe("Dehydration", () => {
|
||||
test.skip(isDendrite, "does not yet support dehydration v2");
|
||||
|
||||
test("Verify device and reset creates dehydrated device", async ({ page, user, credentials, app }, workerInfo) => {
|
||||
// Verify the device by resetting the identity key, and then set up recovery (which will create SSSS, and dehydrated device)
|
||||
// Verify the device by resetting the key (which will create SSSS, and dehydrated device)
|
||||
|
||||
const securityTab = await app.settings.openUserSettings("Security & Privacy");
|
||||
await expect(securityTab.getByText("Offline device enabled")).not.toBeVisible();
|
||||
|
||||
await app.closeDialog();
|
||||
|
||||
// Reset the identity key
|
||||
// Verify the device by resetting the key
|
||||
const settings = await app.settings.openUserSettings("Encryption");
|
||||
await settings.getByRole("button", { name: "Verify this device" }).click();
|
||||
await page.getByRole("button", { name: "Proceed with reset" }).click();
|
||||
await page.getByRole("button", { name: "Continue" }).click();
|
||||
|
||||
// Set up recovery
|
||||
await page.getByRole("button", { name: "Set up recovery" }).click();
|
||||
await page.getByRole("button", { name: "Copy" }).click();
|
||||
await page.getByRole("button", { name: "Continue" }).click();
|
||||
const recoveryKey = await page.getByTestId("recoveryKey").innerText();
|
||||
await page.getByRole("button", { name: "Continue" }).click();
|
||||
await page.getByRole("textbox").fill(recoveryKey);
|
||||
await page.getByRole("button", { name: "Finish set up" }).click();
|
||||
await page.getByRole("button", { name: "Close" }).click();
|
||||
await page.getByRole("button", { name: "Done" }).click();
|
||||
|
||||
await expectDehydratedDeviceEnabled(app);
|
||||
|
||||
@@ -86,7 +80,7 @@ test.describe("Dehydration", () => {
|
||||
await expectDehydratedDeviceEnabled(app);
|
||||
});
|
||||
|
||||
test("Reset identity during login and set up recovery re-creates dehydrated device", async ({
|
||||
test("Reset recovery key during login re-creates dehydrated device", async ({
|
||||
page,
|
||||
homeserver,
|
||||
app,
|
||||
@@ -105,26 +99,16 @@ test.describe("Dehydration", () => {
|
||||
// Log in our client
|
||||
await logIntoElement(page, credentials);
|
||||
|
||||
// Oh no, we forgot our recovery key - reset our identity
|
||||
// Oh no, we forgot our recovery key
|
||||
await page.locator(".mx_AuthPage").getByRole("button", { name: "Reset all" }).click();
|
||||
await expect(
|
||||
page.getByRole("heading", { name: "Are you sure you want to reset your identity?" }),
|
||||
).toBeVisible();
|
||||
await page.getByRole("button", { name: "Continue", exact: true }).click();
|
||||
await page.getByPlaceholder("Password").fill(credentials.password);
|
||||
await page.getByRole("button", { name: "Continue" }).click();
|
||||
await page.locator(".mx_AuthPage").getByRole("button", { name: "Proceed with reset" }).click();
|
||||
|
||||
// And set up recovery
|
||||
const settings = await app.settings.openUserSettings("Encryption");
|
||||
await settings.getByRole("button", { name: "Set up recovery" }).click();
|
||||
await settings.getByRole("button", { name: "Continue" }).click();
|
||||
const recoveryKey = await settings.getByTestId("recoveryKey").innerText();
|
||||
await settings.getByRole("button", { name: "Continue" }).click();
|
||||
await settings.getByRole("textbox").fill(recoveryKey);
|
||||
await settings.getByRole("button", { name: "Finish set up" }).click();
|
||||
await completeCreateSecretStorageDialog(page, { accountPassword: credentials.password });
|
||||
|
||||
// There should be a brand new dehydrated device
|
||||
await expectDehydratedDeviceEnabled(app);
|
||||
const dehydratedDeviceIds = await getDehydratedDeviceIds(app.client);
|
||||
expect(dehydratedDeviceIds.length).toBe(1);
|
||||
expect(dehydratedDeviceIds[0]).not.toEqual(initialDehydratedDeviceIds[0]);
|
||||
});
|
||||
|
||||
test("'Reset cryptographic identity' removes dehydrated device", async ({ page, homeserver, app, credentials }) => {
|
||||
|
||||
@@ -5,11 +5,8 @@
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { type Visibility } from "matrix-js-sdk/src/matrix";
|
||||
import { type Locator, type Page } from "@playwright/test";
|
||||
|
||||
import { expect, test } from "../../../element-web-test";
|
||||
import { SettingLevel } from "../../../../src/settings/SettingLevel";
|
||||
import type { Page } from "@playwright/test";
|
||||
|
||||
test.describe("Room list filters and sort", () => {
|
||||
test.use({
|
||||
@@ -21,18 +18,10 @@ test.describe("Room list filters and sort", () => {
|
||||
labsFlags: ["feature_new_room_list"],
|
||||
});
|
||||
|
||||
function getPrimaryFilters(page: Page): Locator {
|
||||
function getPrimaryFilters(page: Page) {
|
||||
return page.getByRole("listbox", { name: "Room list filters" });
|
||||
}
|
||||
|
||||
function getSecondaryFilters(page: Page): Locator {
|
||||
return page.getByRole("button", { name: "Filter" });
|
||||
}
|
||||
|
||||
function getRoomOptionsMenu(page: Page): Locator {
|
||||
return page.getByRole("button", { name: "Room Options" });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the room list
|
||||
* @param page
|
||||
@@ -46,65 +35,6 @@ test.describe("Room list filters and sort", () => {
|
||||
await app.closeNotificationToast();
|
||||
});
|
||||
|
||||
test("Tombstoned rooms are not shown even when they receive updates", async ({ page, app, bot }) => {
|
||||
// This bug shows up with this setting turned on
|
||||
await app.settings.setValue("Spaces.allRoomsInHome", null, SettingLevel.DEVICE, true);
|
||||
|
||||
/*
|
||||
We will first create a room named 'Old Room' and will invite the bot user to this room.
|
||||
We will also send a simple message in this room.
|
||||
*/
|
||||
const oldRoomId = await app.client.createRoom({ name: "Old Room" });
|
||||
await app.client.inviteUser(oldRoomId, bot.credentials.userId);
|
||||
await bot.joinRoom(oldRoomId);
|
||||
const response = await app.client.sendMessage(oldRoomId, "Hello!");
|
||||
|
||||
/*
|
||||
At this point, we haven't done anything interesting.
|
||||
So we expect 'Old Room' to show up in the room list.
|
||||
*/
|
||||
const roomListView = getRoomList(page);
|
||||
const oldRoomTile = roomListView.getByRole("gridcell", { name: "Open room Old Room" });
|
||||
await expect(oldRoomTile).toBeVisible();
|
||||
|
||||
/*
|
||||
Now let's tombstone 'Old Room'.
|
||||
First we create a new room ('New Room') with the predecessor set to the old room..
|
||||
*/
|
||||
const newRoomId = await bot.createRoom({
|
||||
name: "New Room",
|
||||
creation_content: {
|
||||
predecessor: {
|
||||
event_id: response.event_id,
|
||||
room_id: oldRoomId,
|
||||
},
|
||||
},
|
||||
visibility: "public" as Visibility,
|
||||
});
|
||||
|
||||
/*
|
||||
Now we can send the tombstone event itself to the 'Old Room'.
|
||||
*/
|
||||
await app.client.sendStateEvent(oldRoomId, "m.room.tombstone", {
|
||||
body: "This room has been replaced",
|
||||
replacement_room: newRoomId,
|
||||
});
|
||||
|
||||
// Let's join the replaced room.
|
||||
await app.client.joinRoom(newRoomId);
|
||||
|
||||
// We expect 'Old Room' to be hidden from the room list.
|
||||
await expect(oldRoomTile).not.toBeVisible();
|
||||
|
||||
/*
|
||||
Let's say some user in the 'Old Room' changes their display name.
|
||||
This will send events to the all the rooms including 'Old Room'.
|
||||
Nevertheless, the replaced room should not be shown in the room list.
|
||||
*/
|
||||
await bot.setDisplayName("MyNewName");
|
||||
await expect(oldRoomTile).not.toBeVisible();
|
||||
});
|
||||
|
||||
test.describe("Scroll behaviour", () => {
|
||||
test("should scroll to the top of list when filter is applied and active room is not in filtered list", async ({
|
||||
page,
|
||||
@@ -176,11 +106,6 @@ test.describe("Room list filters and sort", () => {
|
||||
await app.client.evaluate(async (client, favouriteId) => {
|
||||
await client.setRoomTag(favouriteId, "m.favourite", { order: 0.5 });
|
||||
}, favouriteId);
|
||||
|
||||
const lowPrioId = await app.client.createRoom({ name: "Low prio room" });
|
||||
await app.client.evaluate(async (client, id) => {
|
||||
await client.setRoomTag(id, "m.lowpriority", { order: 0.5 });
|
||||
}, lowPrioId);
|
||||
});
|
||||
|
||||
test("should filter the list (with primary filters)", { tag: "@screenshot" }, async ({ page, app, user }) => {
|
||||
@@ -212,66 +137,31 @@ test.describe("Room list filters and sort", () => {
|
||||
await expect(roomList.getByRole("gridcell", { name: "unread room" })).toBeVisible();
|
||||
await expect(roomList.getByRole("gridcell", { name: "favourite room" })).toBeVisible();
|
||||
await expect(roomList.getByRole("gridcell", { name: "empty room" })).toBeVisible();
|
||||
expect(await roomList.locator("role=gridcell").count()).toBe(4);
|
||||
expect(await roomList.locator("role=gridcell").count()).toBe(3);
|
||||
});
|
||||
|
||||
test("should filter the list (with secondary filters)", { tag: "@screenshot" }, async ({ page, app, user }) => {
|
||||
const roomList = getRoomList(page);
|
||||
const secondaryFilters = getSecondaryFilters(page);
|
||||
await secondaryFilters.click();
|
||||
|
||||
await expect(page.getByRole("menu", { name: "Filter" })).toMatchScreenshot("filter-menu.png");
|
||||
|
||||
await page.getByRole("menuitem", { name: "Low priority" }).click();
|
||||
await expect(roomList.getByRole("gridcell", { name: "Low prio room" })).toBeVisible();
|
||||
expect(await roomList.locator("role=gridcell").count()).toBe(1);
|
||||
});
|
||||
|
||||
test(
|
||||
"unread filter should only match unread rooms that have a count",
|
||||
{ tag: "@screenshot" },
|
||||
async ({ page, app, bot }) => {
|
||||
const roomListView = getRoomList(page);
|
||||
|
||||
// Let's configure unread dm room so that we only get notification for mentions and keywords
|
||||
await app.viewRoomById(unReadDmId);
|
||||
await app.settings.openRoomSettings("Notifications");
|
||||
await page.getByText("@mentions & keywords").click();
|
||||
await app.settings.closeDialog();
|
||||
|
||||
// Let's open a room other than unread room or unread dm
|
||||
await roomListView.getByRole("gridcell", { name: "Open room favourite room" }).click();
|
||||
|
||||
// Let's make the bot send a new message in both rooms
|
||||
await bot.sendMessage(unReadDmId, "Hello!");
|
||||
await bot.sendMessage(unReadRoomId, "Hello!");
|
||||
|
||||
// Let's activate the unread filter now
|
||||
await page.getByRole("option", { name: "Unread" }).click();
|
||||
|
||||
// Unread filter should only show unread room and not unread dm!
|
||||
const unreadDm = roomListView.getByRole("gridcell", { name: "Open room unread room" });
|
||||
await expect(unreadDm).toBeVisible();
|
||||
await expect(unreadDm).toMatchScreenshot("unread-dm.png");
|
||||
await expect(roomListView.getByRole("gridcell", { name: "Open room unread dm" })).not.toBeVisible();
|
||||
},
|
||||
);
|
||||
|
||||
test("should sort the room list alphabetically", async ({ page }) => {
|
||||
test("unread filter should only match unread rooms that have a count", async ({ page, app, bot }) => {
|
||||
const roomListView = getRoomList(page);
|
||||
|
||||
await getRoomOptionsMenu(page).click();
|
||||
await page.getByRole("menuitemradio", { name: "A-Z" }).click();
|
||||
// Let's configure unread dm room so that we only get notification for mentions and keywords
|
||||
await app.viewRoomById(unReadDmId);
|
||||
await app.settings.openRoomSettings("Notifications");
|
||||
await page.getByText("@mentions & keywords").click();
|
||||
await app.settings.closeDialog();
|
||||
|
||||
await expect(roomListView.getByRole("gridcell").first()).toHaveText(/empty room/);
|
||||
});
|
||||
|
||||
test("should move room to the top on message when sorting by activity", async ({ page, bot }) => {
|
||||
const roomListView = getRoomList(page);
|
||||
// Let's open a room other than unread room or unread dm
|
||||
await roomListView.getByRole("gridcell", { name: "Open room favourite room" }).click();
|
||||
|
||||
// Let's make the bot send a new message in both rooms
|
||||
await bot.sendMessage(unReadDmId, "Hello!");
|
||||
await bot.sendMessage(unReadRoomId, "Hello!");
|
||||
|
||||
await expect(roomListView.getByRole("gridcell").first()).toHaveText(/unread dm/);
|
||||
// Let's activate the unread filter now
|
||||
await page.getByRole("option", { name: "Unread" }).click();
|
||||
|
||||
// Unread filter should only show unread room and not unread dm!
|
||||
await expect(roomListView.getByRole("gridcell", { name: "Open room unread room" })).toBeVisible();
|
||||
await expect(roomListView.getByRole("gridcell", { name: "Open room unread dm" })).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -30,9 +30,6 @@ test.describe("Room list panel", () => {
|
||||
for (let i = 0; i < 20; i++) {
|
||||
await app.client.createRoom({ name: `room${i}` });
|
||||
}
|
||||
|
||||
// focus the user menu to avoid to have hover decoration
|
||||
await page.getByRole("button", { name: "User menu" }).focus();
|
||||
});
|
||||
|
||||
test("should render the room list panel", { tag: "@screenshot" }, async ({ page, app, user }) => {
|
||||
@@ -41,10 +38,4 @@ test.describe("Room list panel", () => {
|
||||
await expect(roomListView.getByRole("gridcell", { name: "Open room room19" })).toBeVisible();
|
||||
await expect(roomListView).toMatchScreenshot("room-list-panel.png");
|
||||
});
|
||||
|
||||
test("should respond to small screen sizes", { tag: "@screenshot" }, async ({ page }) => {
|
||||
await page.setViewportSize({ width: 575, height: 600 });
|
||||
const roomListPanel = page.getByTestId("room-list-panel");
|
||||
await expect(roomListPanel).toMatchScreenshot("room-list-panel-smallscreen.png");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -29,9 +29,6 @@ test.describe("Room list", () => {
|
||||
test.beforeEach(async ({ page, app, user }) => {
|
||||
// The notification toast is displayed above the search section
|
||||
await app.closeNotificationToast();
|
||||
|
||||
// focus the user menu to avoid to have hover decoration
|
||||
await page.getByRole("button", { name: "User menu" }).focus();
|
||||
});
|
||||
|
||||
test.describe("Room list", () => {
|
||||
@@ -46,8 +43,7 @@ test.describe("Room list", () => {
|
||||
await expect(roomListView.getByRole("gridcell", { name: "Open room room29" })).toBeVisible();
|
||||
await expect(roomListView).toMatchScreenshot("room-list.png");
|
||||
|
||||
// Put focus on the room list
|
||||
await roomListView.getByRole("gridcell", { name: "Open room room29" }).click();
|
||||
await roomListView.hover();
|
||||
// Scroll to the end of the room list
|
||||
await page.mouse.wheel(0, 1000);
|
||||
await expect(roomListView.getByRole("gridcell", { name: "Open room room0" })).toBeVisible();
|
||||
@@ -109,10 +105,13 @@ test.describe("Room list", () => {
|
||||
// It should make the room muted
|
||||
await page.getByRole("menuitem", { name: "Mute room" }).click();
|
||||
|
||||
// Put focus on the room list
|
||||
await roomListView.getByRole("gridcell", { name: "Open room room28" }).click();
|
||||
// Scroll to the end of the room list
|
||||
await page.mouse.wheel(0, 1000);
|
||||
// Remove hover on the room list item
|
||||
await roomListView.hover();
|
||||
|
||||
// Scroll to the bottom of the list
|
||||
await page.getByRole("grid", { name: "Room list" }).evaluate((e) => {
|
||||
e.scrollTop = e.scrollHeight;
|
||||
});
|
||||
|
||||
// The room decoration should have the muted icon
|
||||
await expect(roomItem.getByTestId("notification-decoration")).toBeVisible();
|
||||
@@ -130,8 +129,7 @@ test.describe("Room list", () => {
|
||||
|
||||
test("should scroll to the current room", async ({ page, app, user }) => {
|
||||
const roomListView = getRoomList(page);
|
||||
// Put focus on the room list
|
||||
await roomListView.getByRole("gridcell", { name: "Open room room29" }).click();
|
||||
await roomListView.hover();
|
||||
// Scroll to the end of the room list
|
||||
await page.mouse.wheel(0, 1000);
|
||||
|
||||
@@ -144,98 +142,6 @@ test.describe("Room list", () => {
|
||||
await filters.getByRole("option", { name: "People" }).click();
|
||||
await expect(roomListView.getByRole("gridcell", { name: "Open room room0" })).toBeVisible();
|
||||
});
|
||||
|
||||
test.describe("Shortcuts", () => {
|
||||
test("should select the next room", async ({ page, app, user }) => {
|
||||
const roomListView = getRoomList(page);
|
||||
await roomListView.getByRole("gridcell", { name: "Open room room29" }).click();
|
||||
await page.keyboard.press("Alt+ArrowDown");
|
||||
|
||||
await expect(page.getByRole("heading", { name: "room28", level: 1 })).toBeVisible();
|
||||
});
|
||||
|
||||
test("should select the previous room", async ({ page, app, user }) => {
|
||||
const roomListView = getRoomList(page);
|
||||
await roomListView.getByRole("gridcell", { name: "Open room room28" }).click();
|
||||
await page.keyboard.press("Alt+ArrowUp");
|
||||
|
||||
await expect(page.getByRole("heading", { name: "room29", level: 1 })).toBeVisible();
|
||||
});
|
||||
|
||||
test("should select the last room", async ({ page, app, user }) => {
|
||||
const roomListView = getRoomList(page);
|
||||
await roomListView.getByRole("gridcell", { name: "Open room room29" }).click();
|
||||
await page.keyboard.press("Alt+ArrowUp");
|
||||
|
||||
await expect(page.getByRole("heading", { name: "room0", level: 1 })).toBeVisible();
|
||||
});
|
||||
|
||||
test("should select the next unread room", async ({ page, app, user, bot }) => {
|
||||
const roomListView = getRoomList(page);
|
||||
|
||||
const roomId = await app.client.createRoom({ name: "1 notification" });
|
||||
await app.client.inviteUser(roomId, bot.credentials.userId);
|
||||
await bot.joinRoom(roomId);
|
||||
await bot.sendMessage(roomId, "I am a robot. Beep.");
|
||||
|
||||
await roomListView.getByRole("gridcell", { name: "Open room room20" }).click();
|
||||
|
||||
await page.keyboard.press("Alt+Shift+ArrowDown");
|
||||
|
||||
await expect(page.getByRole("heading", { name: "1 notification", level: 1 })).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Keyboard navigation", () => {
|
||||
test("should navigate to the room list", async ({ page, app, user }) => {
|
||||
const roomListView = getRoomList(page);
|
||||
|
||||
const room29 = roomListView.getByRole("gridcell", { name: "Open room room29" });
|
||||
const room28 = roomListView.getByRole("gridcell", { name: "Open room room28" });
|
||||
|
||||
// open the room
|
||||
await room29.click();
|
||||
// put focus back on the room list item
|
||||
await room29.click();
|
||||
await expect(room29).toBeFocused();
|
||||
|
||||
await page.keyboard.press("ArrowDown");
|
||||
await expect(room28).toBeFocused();
|
||||
await expect(room29).not.toBeFocused();
|
||||
|
||||
await page.keyboard.press("ArrowUp");
|
||||
await expect(room29).toBeFocused();
|
||||
await expect(room28).not.toBeFocused();
|
||||
});
|
||||
|
||||
test("should navigate to the notification menu", async ({ page, app, user }) => {
|
||||
const roomListView = getRoomList(page);
|
||||
const room29 = roomListView.getByRole("gridcell", { name: "Open room room29" });
|
||||
const moreButton = room29.getByRole("button", { name: "More options" });
|
||||
const notificationButton = room29.getByRole("button", { name: "Notification options" });
|
||||
|
||||
await room29.click();
|
||||
// put focus back on the room list item
|
||||
await room29.click();
|
||||
await page.keyboard.press("Tab");
|
||||
await expect(moreButton).toBeFocused();
|
||||
await page.keyboard.press("Tab");
|
||||
await expect(notificationButton).toBeFocused();
|
||||
|
||||
// Open the menu
|
||||
await notificationButton.click();
|
||||
// Wait for the menu to be open
|
||||
await expect(page.getByRole("menuitem", { name: "Match default settings" })).toHaveAttribute(
|
||||
"aria-selected",
|
||||
"true",
|
||||
);
|
||||
|
||||
// Close the menu
|
||||
await page.keyboard.press("Escape");
|
||||
// Focus should be back on the room list item
|
||||
await expect(room29).toBeFocused();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Avatar decoration", () => {
|
||||
@@ -244,10 +150,6 @@ test.describe("Room list", () => {
|
||||
test("should be a public room", { tag: "@screenshot" }, async ({ page, app, user }) => {
|
||||
// @ts-ignore Visibility enum is not accessible
|
||||
await app.client.createRoom({ name: "public room", visibility: "public" });
|
||||
|
||||
// focus the user menu to avoid to have hover decoration
|
||||
await page.getByRole("button", { name: "User menu" }).focus();
|
||||
|
||||
const roomListView = getRoomList(page);
|
||||
const publicRoom = roomListView.getByRole("gridcell", { name: "public room" });
|
||||
|
||||
@@ -263,10 +165,6 @@ test.describe("Room list", () => {
|
||||
|
||||
const roomListView = getRoomList(page);
|
||||
const videoRoom = roomListView.getByRole("gridcell", { name: "video room" });
|
||||
|
||||
// focus the user menu to avoid to have hover decoration
|
||||
await page.getByRole("button", { name: "User menu" }).focus();
|
||||
|
||||
await expect(videoRoom).toBeVisible();
|
||||
await expect(videoRoom).toMatchScreenshot("room-list-item-video.png");
|
||||
});
|
||||
@@ -332,26 +230,6 @@ test.describe("Room list", () => {
|
||||
await expect(room).toMatchScreenshot("room-list-item-mention.png");
|
||||
});
|
||||
|
||||
test("should render a message preview", { tag: "@screenshot" }, async ({ page, app, user, bot }) => {
|
||||
const roomListView = getRoomList(page);
|
||||
|
||||
await page.getByRole("button", { name: "Room Options" }).click();
|
||||
await page.getByRole("menuitemcheckbox", { name: "Show message previews" }).click();
|
||||
|
||||
const roomId = await app.client.createRoom({ name: "activity" });
|
||||
|
||||
// focus the user menu to avoid to have hover decoration
|
||||
await page.getByRole("button", { name: "User menu" }).focus();
|
||||
|
||||
await app.client.inviteUser(roomId, bot.credentials.userId);
|
||||
await bot.joinRoom(roomId);
|
||||
await bot.sendMessage(roomId, "I am a robot. Beep.");
|
||||
|
||||
const room = roomListView.getByRole("gridcell", { name: "activity" });
|
||||
await expect(room.getByText("I am a robot. Beep.")).toBeVisible();
|
||||
await expect(room).toMatchScreenshot("room-list-item-message-preview.png");
|
||||
});
|
||||
|
||||
test("should render an activity decoration", { tag: "@screenshot" }, async ({ page, app, user, bot }) => {
|
||||
const roomListView = getRoomList(page);
|
||||
|
||||
@@ -391,8 +269,8 @@ test.describe("Room list", () => {
|
||||
await room.getByRole("button", { name: "More Options" }).click();
|
||||
await page.getByRole("menuitem", { name: "mark as unread" }).click();
|
||||
|
||||
// focus the user menu to avoid to have hover decoration
|
||||
await page.getByRole("button", { name: "User menu" }).focus();
|
||||
// Remove hover on the room list item
|
||||
await roomListView.hover();
|
||||
|
||||
await expect(room).toMatchScreenshot("room-list-item-mark-as-unread.png");
|
||||
});
|
||||
|
||||
@@ -288,43 +288,6 @@ test.describe("Login", () => {
|
||||
await expect(h1).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test("Can reset identity to become verified", async ({ page, homeserver, request, credentials }) => {
|
||||
// Log in
|
||||
const res = await request.post(`${homeserver.baseUrl}/_matrix/client/v3/keys/device_signing/upload`, {
|
||||
headers: { Authorization: `Bearer ${credentials.accessToken}` },
|
||||
data: DEVICE_SIGNING_KEYS_BODY,
|
||||
});
|
||||
if (!res.ok()) {
|
||||
console.log(`Uploading dummy keys failed with HTTP status ${res.status}`, await res.json());
|
||||
throw new Error("Uploading dummy keys failed");
|
||||
}
|
||||
|
||||
await page.goto("/");
|
||||
await login(page, homeserver, credentials);
|
||||
|
||||
await expect(page.getByRole("heading", { name: "Verify this device", level: 1 })).toBeVisible();
|
||||
|
||||
// Start the reset process
|
||||
await page.getByRole("button", { name: "Proceed with reset" }).click();
|
||||
|
||||
// First try cancelling and restarting
|
||||
await page.getByRole("button", { name: "Cancel" }).click();
|
||||
await page.getByRole("button", { name: "Proceed with reset" }).click();
|
||||
|
||||
// Then click outside the dialog and restart
|
||||
await page.getByRole("link", { name: "Powered by Matrix" }).click({ force: true });
|
||||
await page.getByRole("button", { name: "Proceed with reset" }).click();
|
||||
|
||||
// Finally we actually continue
|
||||
await page.getByRole("button", { name: "Continue" }).click();
|
||||
await page.getByPlaceholder("Password").fill(credentials.password);
|
||||
await page.getByRole("button", { name: "Continue" }).click();
|
||||
|
||||
// We end up at the Home screen
|
||||
await expect(page).toHaveURL(/\/#\/home$/, { timeout: 10000 });
|
||||
await expect(page.getByRole("heading", { name: "Welcome Dave", exact: true })).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ test.describe("Module loading", () => {
|
||||
test.describe("Example Module", () => {
|
||||
test.use({
|
||||
config: {
|
||||
brand: "TestBrand",
|
||||
modules: ["/modules/example-module.js"],
|
||||
},
|
||||
page: async ({ page }, use) => {
|
||||
@@ -26,31 +25,11 @@ test.describe("Module loading", () => {
|
||||
},
|
||||
});
|
||||
|
||||
const testCases = [
|
||||
["en", "TestBrand module loading successful!"],
|
||||
["de", "TestBrand-Module erfolgreich geladen!"],
|
||||
];
|
||||
|
||||
for (const [lang, message] of testCases) {
|
||||
test.describe(`language-${lang}`, () => {
|
||||
test.use({
|
||||
config: async ({ config }, use) => {
|
||||
await use({
|
||||
...config,
|
||||
setting_defaults: {
|
||||
language: lang,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
test("should show alert", async ({ page }) => {
|
||||
const dialogPromise = page.waitForEvent("dialog");
|
||||
await page.goto("/");
|
||||
const dialog = await dialogPromise;
|
||||
expect(dialog.message()).toBe(message);
|
||||
});
|
||||
});
|
||||
}
|
||||
test("should show alert", async ({ page }) => {
|
||||
const dialogPromise = page.waitForEvent("dialog");
|
||||
await page.goto("/");
|
||||
const dialog = await dialogPromise;
|
||||
expect(dialog.message()).toBe("Testing module loading successful!");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -88,6 +88,7 @@ test.describe("OIDC Native", { tag: ["@no-firefox", "@no-webkit"] }, () => {
|
||||
await expect(page.getByText("Welcome")).toBeVisible();
|
||||
await page.goto("about:blank");
|
||||
|
||||
// @ts-expect-error
|
||||
const result = await mas.manage("kill-sessions", userId);
|
||||
expect(result.output).toContain("Ended 1 active OAuth 2.0 session");
|
||||
|
||||
|
||||
@@ -15,34 +15,20 @@ test.describe("Release announcement", () => {
|
||||
feature_release_announcement: true,
|
||||
},
|
||||
},
|
||||
room: async ({ app, user }, use) => {
|
||||
const roomId = await app.client.createRoom({
|
||||
name: "Test room",
|
||||
});
|
||||
await app.viewRoomById(roomId);
|
||||
await use({ roomId });
|
||||
},
|
||||
labsFlags: ["threadsActivityCentre"],
|
||||
});
|
||||
|
||||
test(
|
||||
"should display the pinned messages release announcement",
|
||||
{ tag: "@screenshot" },
|
||||
async ({ page, app, room, util }) => {
|
||||
await app.toggleRoomInfoPanel();
|
||||
test("should display the release announcement process", { tag: "@screenshot" }, async ({ page, app, util }) => {
|
||||
// The TAC release announcement should be displayed
|
||||
await util.assertReleaseAnnouncementIsVisible("Threads Activity Centre");
|
||||
// Hide the release announcement
|
||||
await util.markReleaseAnnouncementAsRead("Threads Activity Centre");
|
||||
await util.assertReleaseAnnouncementIsNotVisible("Threads Activity Centre");
|
||||
|
||||
const name = "All new pinned messages";
|
||||
|
||||
// The release announcement should be displayed
|
||||
await util.assertReleaseAnnouncementIsVisible(name);
|
||||
// Hide the release announcement
|
||||
await util.markReleaseAnnouncementAsRead(name);
|
||||
await util.assertReleaseAnnouncementIsNotVisible(name);
|
||||
|
||||
await page.reload();
|
||||
await app.toggleRoomInfoPanel();
|
||||
await expect(page.getByRole("menuitem", { name: "Pinned messages" })).toBeVisible();
|
||||
// Check that once the release announcement has been marked as viewed, it does not appear again
|
||||
await util.assertReleaseAnnouncementIsNotVisible(name);
|
||||
},
|
||||
);
|
||||
await page.reload();
|
||||
// Wait for EW to load
|
||||
await expect(page.getByRole("navigation", { name: "Spaces" })).toBeVisible();
|
||||
// Check that once the release announcement has been marked as viewed, it does not appear again
|
||||
await util.assertReleaseAnnouncementIsNotVisible("Threads Activity Centre");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,7 +11,6 @@ import { type Locator, type Page } from "@playwright/test";
|
||||
import { test, expect } from "../../element-web-test";
|
||||
import { checkRoomSummaryCard, viewRoomSummaryByName } from "./utils";
|
||||
import { isDendrite } from "../../plugins/homeserver/dendrite";
|
||||
import { Bot } from "../../pages/bot";
|
||||
|
||||
const ROOM_NAME = "Test room";
|
||||
const ROOM_NAME_LONG =
|
||||
@@ -22,23 +21,20 @@ const ROOM_NAME_LONG =
|
||||
"officia deserunt mollit anim id est laborum.";
|
||||
const SPACE_NAME = "Test space";
|
||||
const NAME = "Alice";
|
||||
const LONG_NAME = "Bob long long long long long long long long long long long long long long long name";
|
||||
|
||||
const ROOM_ADDRESS_LONG =
|
||||
"loremIpsumDolorSitAmetConsecteturAdipisicingElitSedDoEiusmodTemporIncididuntUtLaboreEtDoloreMagnaAliqua";
|
||||
|
||||
function getMemberTileByName(page: Page, name: string): Locator {
|
||||
return page.locator(".mx_MemberListView .mx_MemberTileView_name").filter({ hasText: name });
|
||||
return page.locator(`.mx_MemberTileView, [title="${name}"]`);
|
||||
}
|
||||
|
||||
test.describe("RightPanel", () => {
|
||||
let testRoomId: string;
|
||||
test.use({
|
||||
displayName: NAME,
|
||||
});
|
||||
|
||||
test.beforeEach(async ({ app, user }) => {
|
||||
testRoomId = await app.client.createRoom({ name: ROOM_NAME });
|
||||
await app.client.createRoom({ name: ROOM_NAME });
|
||||
await app.client.createSpace({ name: SPACE_NAME });
|
||||
});
|
||||
|
||||
@@ -81,12 +77,10 @@ test.describe("RightPanel", () => {
|
||||
await expect(page.locator(".mx_RightPanel")).toMatchScreenshot("with-leave-room.png");
|
||||
});
|
||||
|
||||
test("should handle clicking add widgets", { tag: "@screenshot" }, async ({ page, app }) => {
|
||||
test("should handle clicking add widgets", async ({ page, app }) => {
|
||||
await viewRoomSummaryByName(page, app, ROOM_NAME);
|
||||
|
||||
await page.getByRole("menuitem", { name: "Extensions" }).click();
|
||||
await expect(page.getByTestId("right-panel")).toMatchScreenshot("with-extensions.png");
|
||||
|
||||
await page.getByRole("button", { name: "Add extensions" }).click();
|
||||
await expect(page.locator(".mx_IntegrationManager")).toBeVisible();
|
||||
});
|
||||
@@ -140,37 +134,6 @@ test.describe("RightPanel", () => {
|
||||
await page.getByLabel("Room info").nth(1).click();
|
||||
await checkRoomSummaryCard(page, ROOM_NAME);
|
||||
});
|
||||
|
||||
test(
|
||||
"should handle viewing long room member name",
|
||||
{ tag: "@screenshot" },
|
||||
async ({ page, homeserver, app }) => {
|
||||
const bobLongName = new Bot(page, homeserver, { displayName: LONG_NAME });
|
||||
await bobLongName.prepareClient();
|
||||
await app.client.inviteUser(testRoomId, bobLongName.credentials.userId);
|
||||
await bobLongName.joinRoom(testRoomId);
|
||||
|
||||
await viewRoomSummaryByName(page, app, ROOM_NAME);
|
||||
|
||||
await page.locator(".mx_RightPanel").getByRole("menuitem", { name: "People" }).click();
|
||||
await expect(page.locator(".mx_MemberListView")).toBeVisible();
|
||||
|
||||
await getMemberTileByName(page, LONG_NAME).click();
|
||||
await expect(page.locator(".mx_UserInfo")).toBeVisible();
|
||||
await expect(page.locator(".mx_UserInfo_profile").getByText(LONG_NAME)).toBeVisible();
|
||||
|
||||
await expect(page.locator(".mx_UserInfo")).toMatchScreenshot("with-long-name.png", {
|
||||
mask: [page.locator(".mx_UserInfo_profile_mxid")],
|
||||
css: `
|
||||
/* Use monospace font for consistent mask width */
|
||||
.mx_UserInfo_profile_mxid {
|
||||
font-family: Inconsolata !important;
|
||||
}
|
||||
`,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test.describe("room reporting", () => {
|
||||
test.skip(isDendrite, "Dendrite does not implement room reporting");
|
||||
test("should handle reporting a room", { tag: "@screenshot" }, async ({ page, app }) => {
|
||||
|
||||
@@ -19,14 +19,7 @@ test.describe("Invites", () => {
|
||||
const roomId = await bot.createRoom({ is_direct: true });
|
||||
await bot.inviteUser(roomId, user.userId);
|
||||
await app.viewRoomByName("Bob");
|
||||
await expect(page.locator(".mx_RoomView")).toMatchScreenshot("Invites_room_view.png", {
|
||||
// Hide the mxid, which is not stable.
|
||||
css: `
|
||||
.mx_RoomPreviewBar_inviter_mxid {
|
||||
display: none !important;
|
||||
}
|
||||
`,
|
||||
});
|
||||
await expect(page.locator(".mx_RoomView")).toMatchScreenshot("Invites_room_view.png");
|
||||
});
|
||||
|
||||
test("should be able to decline an invite", async ({ page, homeserver, user, bot, app }) => {
|
||||
|
||||
@@ -19,162 +19,126 @@ import {
|
||||
test.describe("Encryption tab", () => {
|
||||
test.use({ displayName: "Alice" });
|
||||
|
||||
test.describe("when encryption is set up", () => {
|
||||
let recoveryKey: GeneratedSecretStorageKey;
|
||||
let expectedBackupVersion: string;
|
||||
let recoveryKey: GeneratedSecretStorageKey;
|
||||
let expectedBackupVersion: string;
|
||||
|
||||
test.beforeEach(async ({ page, homeserver, credentials }) => {
|
||||
// The bot bootstraps cross-signing, creates a key backup and sets up a recovery key
|
||||
const res = await createBot(page, homeserver, credentials);
|
||||
recoveryKey = res.recoveryKey;
|
||||
expectedBackupVersion = res.expectedBackupVersion;
|
||||
});
|
||||
test.beforeEach(async ({ page, homeserver, credentials }) => {
|
||||
// The bot bootstraps cross-signing, creates a key backup and sets up a recovery key
|
||||
const res = await createBot(page, homeserver, credentials);
|
||||
recoveryKey = res.recoveryKey;
|
||||
expectedBackupVersion = res.expectedBackupVersion;
|
||||
});
|
||||
|
||||
test(
|
||||
"should show a 'Verify this device' button if the device is unverified",
|
||||
{ tag: "@screenshot" },
|
||||
async ({ page, app, util }) => {
|
||||
const dialog = await util.openEncryptionTab();
|
||||
const content = util.getEncryptionTabContent();
|
||||
test(
|
||||
"should show a 'Verify this device' button if the device is unverified",
|
||||
{ tag: "@screenshot" },
|
||||
async ({ page, app, util }) => {
|
||||
const dialog = await util.openEncryptionTab();
|
||||
const content = util.getEncryptionTabContent();
|
||||
|
||||
// The user's device is in an unverified state, therefore the only option available to them here is to verify it
|
||||
const verifyButton = dialog.getByRole("button", { name: "Verify this device" });
|
||||
await expect(verifyButton).toBeVisible();
|
||||
await expect(content).toMatchScreenshot("verify-device-encryption-tab.png");
|
||||
await verifyButton.click();
|
||||
// The user's device is in an unverified state, therefore the only option available to them here is to verify it
|
||||
const verifyButton = dialog.getByRole("button", { name: "Verify this device" });
|
||||
await expect(verifyButton).toBeVisible();
|
||||
await expect(content).toMatchScreenshot("verify-device-encryption-tab.png");
|
||||
await verifyButton.click();
|
||||
|
||||
await util.verifyDevice(recoveryKey);
|
||||
await util.verifyDevice(recoveryKey);
|
||||
|
||||
await expect(content).toMatchScreenshot("default-tab.png", {
|
||||
mask: [content.getByTestId("deviceId"), content.getByTestId("sessionKey")],
|
||||
});
|
||||
await expect(content).toMatchScreenshot("default-tab.png", {
|
||||
mask: [content.getByTestId("deviceId"), content.getByTestId("sessionKey")],
|
||||
});
|
||||
|
||||
// Check that our device is now cross-signed
|
||||
await checkDeviceIsCrossSigned(app);
|
||||
// Check that our device is now cross-signed
|
||||
await checkDeviceIsCrossSigned(app);
|
||||
|
||||
// Check that the current device is connected to key backup
|
||||
// The backup decryption key should be in cache also, as we got it directly from the 4S
|
||||
await checkDeviceIsConnectedKeyBackup(app, expectedBackupVersion, true);
|
||||
},
|
||||
);
|
||||
// Check that the current device is connected to key backup
|
||||
// The backup decryption key should be in cache also, as we got it directly from the 4S
|
||||
await checkDeviceIsConnectedKeyBackup(app, expectedBackupVersion, true);
|
||||
},
|
||||
);
|
||||
|
||||
// Test what happens if the cross-signing secrets are in secret storage but are not cached in the local DB.
|
||||
//
|
||||
// This can happen if we verified another device and secret-gossiping failed, or the other device itself lacked the secrets.
|
||||
// We simulate this case by deleting the cached secrets in the indexedDB.
|
||||
test(
|
||||
"should prompt to enter the recovery key when the secrets are not cached locally",
|
||||
{ tag: "@screenshot" },
|
||||
async ({ page, app, util }) => {
|
||||
await verifySession(app, recoveryKey.encodedPrivateKey);
|
||||
// We need to delete the cached secrets
|
||||
await deleteCachedSecrets(page);
|
||||
|
||||
await util.openEncryptionTab();
|
||||
// We ask the user to enter the recovery key
|
||||
const dialog = util.getEncryptionTabContent();
|
||||
const enterKeyButton = dialog.getByRole("button", { name: "Enter recovery key" });
|
||||
await expect(enterKeyButton).toBeVisible();
|
||||
await expect(dialog).toMatchScreenshot("out-of-sync-recovery.png");
|
||||
await enterKeyButton.click();
|
||||
|
||||
// Fill the recovery key
|
||||
await util.enterRecoveryKey(recoveryKey);
|
||||
await expect(dialog).toMatchScreenshot("default-tab.png", {
|
||||
mask: [dialog.getByTestId("deviceId"), dialog.getByTestId("sessionKey")],
|
||||
});
|
||||
|
||||
// Check that our device is now cross-signed
|
||||
await checkDeviceIsCrossSigned(app);
|
||||
|
||||
// Check that the current device is connected to key backup
|
||||
// The backup decryption key should be in cache also, as we got it directly from the 4S
|
||||
await checkDeviceIsConnectedKeyBackup(app, expectedBackupVersion, true);
|
||||
},
|
||||
);
|
||||
|
||||
test("should display the reset identity panel when the user clicks on 'Forgot recovery key?'", async ({
|
||||
page,
|
||||
app,
|
||||
util,
|
||||
}) => {
|
||||
// Test what happens if the cross-signing secrets are in secret storage but are not cached in the local DB.
|
||||
//
|
||||
// This can happen if we verified another device and secret-gossiping failed, or the other device itself lacked the secrets.
|
||||
// We simulate this case by deleting the cached secrets in the indexedDB.
|
||||
test(
|
||||
"should prompt to enter the recovery key when the secrets are not cached locally",
|
||||
{ tag: "@screenshot" },
|
||||
async ({ page, app, util }) => {
|
||||
await verifySession(app, recoveryKey.encodedPrivateKey);
|
||||
// We need to delete the cached secrets
|
||||
await deleteCachedSecrets(page);
|
||||
|
||||
// The "Key storage is out sync" section is displayed and the user click on the "Forgot recovery key?" button
|
||||
await util.openEncryptionTab();
|
||||
// We ask the user to enter the recovery key
|
||||
const dialog = util.getEncryptionTabContent();
|
||||
await dialog.getByRole("button", { name: "Forgot recovery key?" }).click();
|
||||
const enterKeyButton = dialog.getByRole("button", { name: "Enter recovery key" });
|
||||
await expect(enterKeyButton).toBeVisible();
|
||||
await expect(dialog).toMatchScreenshot("out-of-sync-recovery.png");
|
||||
await enterKeyButton.click();
|
||||
|
||||
// The user is prompted to reset their identity
|
||||
await expect(
|
||||
dialog.getByText("Forgot your recovery key? You’ll need to reset your identity."),
|
||||
).toBeVisible();
|
||||
});
|
||||
// Fill the recovery key
|
||||
await util.enterRecoveryKey(recoveryKey);
|
||||
await expect(dialog).toMatchScreenshot("default-tab.png", {
|
||||
mask: [dialog.getByTestId("deviceId"), dialog.getByTestId("sessionKey")],
|
||||
});
|
||||
|
||||
test("should warn before turning off key storage", { tag: "@screenshot" }, async ({ page, app, util }) => {
|
||||
await verifySession(app, recoveryKey.encodedPrivateKey);
|
||||
await util.openEncryptionTab();
|
||||
// Check that our device is now cross-signed
|
||||
await checkDeviceIsCrossSigned(app);
|
||||
|
||||
await page.getByRole("checkbox", { name: "Allow key storage" }).click();
|
||||
// Check that the current device is connected to key backup
|
||||
// The backup decryption key should be in cache also, as we got it directly from the 4S
|
||||
await checkDeviceIsConnectedKeyBackup(app, expectedBackupVersion, true);
|
||||
},
|
||||
);
|
||||
|
||||
await expect(
|
||||
page.getByRole("heading", { name: "Are you sure you want to turn off key storage and delete it?" }),
|
||||
).toBeVisible();
|
||||
test("should display the reset identity panel when the user clicks on 'Forgot recovery key?'", async ({
|
||||
page,
|
||||
app,
|
||||
util,
|
||||
}) => {
|
||||
await verifySession(app, recoveryKey.encodedPrivateKey);
|
||||
// We need to delete the cached secrets
|
||||
await deleteCachedSecrets(page);
|
||||
|
||||
await expect(util.getEncryptionTabContent()).toMatchScreenshot("delete-key-storage-confirm.png");
|
||||
// The "Key storage is out sync" section is displayed and the user click on the "Forgot recovery key?" button
|
||||
await util.openEncryptionTab();
|
||||
const dialog = util.getEncryptionTabContent();
|
||||
await dialog.getByRole("button", { name: "Forgot recovery key?" }).click();
|
||||
|
||||
const deleteRequestPromises = [
|
||||
page.waitForRequest((req) => req.url().endsWith("/account_data/m.cross_signing.master")),
|
||||
page.waitForRequest((req) => req.url().endsWith("/account_data/m.cross_signing.self_signing")),
|
||||
page.waitForRequest((req) => req.url().endsWith("/account_data/m.cross_signing.user_signing")),
|
||||
page.waitForRequest((req) => req.url().endsWith("/account_data/m.megolm_backup.v1")),
|
||||
page.waitForRequest((req) => req.url().endsWith("/account_data/m.secret_storage.default_key")),
|
||||
page.waitForRequest((req) => req.url().includes("/account_data/m.secret_storage.key.")),
|
||||
];
|
||||
|
||||
await page.getByRole("button", { name: "Delete key storage" }).click();
|
||||
|
||||
await expect(page.getByRole("checkbox", { name: "Allow key storage" })).not.toBeChecked();
|
||||
|
||||
for (const prom of deleteRequestPromises) {
|
||||
const request = await prom;
|
||||
expect(request.method()).toBe("PUT");
|
||||
expect(request.postData()).toBe(JSON.stringify({}));
|
||||
}
|
||||
});
|
||||
// The user is prompted to reset their identity
|
||||
await expect(dialog.getByText("Forgot your recovery key? You’ll need to reset your identity.")).toBeVisible();
|
||||
});
|
||||
|
||||
test.describe("when encryption is not set up", () => {
|
||||
test("'Verify this device' allows us to become verified", async ({
|
||||
page,
|
||||
user,
|
||||
credentials,
|
||||
app,
|
||||
}, workerInfo) => {
|
||||
const settings = await app.settings.openUserSettings("Encryption");
|
||||
test("should warn before turning off key storage", { tag: "@screenshot" }, async ({ page, app, util }) => {
|
||||
await verifySession(app, recoveryKey.encodedPrivateKey);
|
||||
await util.openEncryptionTab();
|
||||
|
||||
// Initially, our device is not verified
|
||||
await expect(settings.getByRole("heading", { name: "Device not verified" })).toBeVisible();
|
||||
await page.getByRole("checkbox", { name: "Allow key storage" }).click();
|
||||
|
||||
// We will reset our identity
|
||||
await settings.getByRole("button", { name: "Verify this device" }).click();
|
||||
await page.getByRole("button", { name: "Proceed with reset" }).click();
|
||||
await expect(
|
||||
page.getByRole("heading", { name: "Are you sure you want to turn off key storage and delete it?" }),
|
||||
).toBeVisible();
|
||||
|
||||
// First try cancelling and restarting
|
||||
await page.getByRole("button", { name: "Cancel" }).click();
|
||||
await page.getByRole("button", { name: "Proceed with reset" }).click();
|
||||
await expect(util.getEncryptionTabContent()).toMatchScreenshot("delete-key-storage-confirm.png");
|
||||
|
||||
// Then click outside the dialog and restart
|
||||
await page.locator("li").filter({ hasText: "Encryption" }).click({ force: true });
|
||||
await page.getByRole("button", { name: "Proceed with reset" }).click();
|
||||
const deleteRequestPromises = [
|
||||
page.waitForRequest((req) => req.url().endsWith("/account_data/m.cross_signing.master")),
|
||||
page.waitForRequest((req) => req.url().endsWith("/account_data/m.cross_signing.self_signing")),
|
||||
page.waitForRequest((req) => req.url().endsWith("/account_data/m.cross_signing.user_signing")),
|
||||
page.waitForRequest((req) => req.url().endsWith("/account_data/m.megolm_backup.v1")),
|
||||
page.waitForRequest((req) => req.url().endsWith("/account_data/m.secret_storage.default_key")),
|
||||
page.waitForRequest((req) => req.url().includes("/account_data/m.secret_storage.key.")),
|
||||
];
|
||||
|
||||
// Finally we actually continue
|
||||
await page.getByRole("button", { name: "Continue" }).click();
|
||||
await page.getByRole("button", { name: "Delete key storage" }).click();
|
||||
|
||||
// Now we are verified, so we see the Key storage toggle
|
||||
await expect(settings.getByRole("heading", { name: "Key storage" })).toBeVisible();
|
||||
});
|
||||
await expect(page.getByRole("checkbox", { name: "Allow key storage" })).not.toBeChecked();
|
||||
|
||||
for (const prom of deleteRequestPromises) {
|
||||
const request = await prom;
|
||||
expect(request.method()).toBe("PUT");
|
||||
expect(request.postData()).toBe(JSON.stringify({}));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -37,15 +37,6 @@ test.describe("Roles & Permissions room settings tab", () => {
|
||||
// Change the role of Alice to Moderator (50)
|
||||
await combobox.selectOption("Moderator");
|
||||
await expect(combobox).toHaveValue("50");
|
||||
|
||||
// Should display a modal to warn that we are demoting the only admin user
|
||||
const modal = await page.locator(".mx_Dialog", {
|
||||
hasText: "Warning",
|
||||
});
|
||||
await expect(modal).toBeVisible();
|
||||
// Click on the continue button in the modal
|
||||
await modal.getByRole("button", { name: "Continue" }).click();
|
||||
|
||||
const respPromise = page.waitForRequest("**/state/**");
|
||||
await applyButton.click();
|
||||
await respPromise;
|
||||
|
||||
@@ -19,6 +19,7 @@ test.describe("Threads Activity Centre", { tag: "@no-firefox" }, () => {
|
||||
test.use({
|
||||
displayName: "Alice",
|
||||
botCreateOpts: { displayName: "Other User" },
|
||||
labsFlags: ["threadsActivityCentre"],
|
||||
});
|
||||
|
||||
test(
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
/*
|
||||
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 * as fs from "node:fs";
|
||||
import { type EventType, type MsgType, type RoomJoinRulesEventContent } from "matrix-js-sdk/src/types";
|
||||
|
||||
import { test, expect } from "../../element-web-test";
|
||||
|
||||
const MEDIA_FILE = fs.readFileSync("playwright/sample-files/riot.png");
|
||||
|
||||
test.describe("Media preview settings", () => {
|
||||
test.use({
|
||||
displayName: "Alan",
|
||||
botCreateOpts: {
|
||||
displayName: "Bob",
|
||||
},
|
||||
room: async ({ app, page, homeserver, bot, user }, use) => {
|
||||
const mxc = (await bot.uploadContent(MEDIA_FILE, { name: "image.png", type: "image/png" })).content_uri;
|
||||
const roomId = await bot.createRoom({
|
||||
name: "Test room",
|
||||
invite: [user.userId],
|
||||
initial_state: [{ type: "m.room.avatar", content: { url: mxc }, state_key: "" }],
|
||||
});
|
||||
await bot.sendEvent(roomId, null, "m.room.message" as EventType, {
|
||||
msgtype: "m.image" as MsgType,
|
||||
body: "image.png",
|
||||
url: mxc,
|
||||
});
|
||||
|
||||
await use({ roomId });
|
||||
},
|
||||
});
|
||||
|
||||
test("should be able to hide avatars of inviters", { tag: "@screenshot" }, async ({ page, app, room, user }) => {
|
||||
let settings = await app.settings.openUserSettings("Preferences");
|
||||
await settings.getByLabel("Hide avatars of room and inviter").click();
|
||||
await app.closeDialog();
|
||||
await app.viewRoomById(room.roomId);
|
||||
await expect(
|
||||
page.getByRole("complementary").filter({ hasText: "Do you want to join Test room" }),
|
||||
).toMatchScreenshot("invite-no-avatar.png", {
|
||||
// Hide the mxid, which is not stable.
|
||||
css: `
|
||||
.mx_RoomPreviewBar_inviter_mxid {
|
||||
display: none !important;
|
||||
}
|
||||
`,
|
||||
});
|
||||
await expect(
|
||||
page.getByRole("tree", { name: "Rooms" }).getByRole("treeitem", { name: "Test room" }),
|
||||
).toMatchScreenshot("invite-room-tree-no-avatar.png");
|
||||
|
||||
// And then go back to being visible
|
||||
settings = await app.settings.openUserSettings("Preferences");
|
||||
await settings.getByLabel("Hide avatars of room and inviter").click();
|
||||
await app.closeDialog();
|
||||
await page.goto("#/home");
|
||||
await app.viewRoomById(room.roomId);
|
||||
await expect(
|
||||
page.getByRole("complementary").filter({ hasText: "Do you want to join Test room" }),
|
||||
).toMatchScreenshot("invite-with-avatar.png", {
|
||||
// Hide the mxid, which is not stable.
|
||||
css: `
|
||||
.mx_RoomPreviewBar_inviter_mxid {
|
||||
display: none !important;
|
||||
}
|
||||
`,
|
||||
});
|
||||
await expect(
|
||||
page.getByRole("tree", { name: "Rooms" }).getByRole("treeitem", { name: "Test room" }),
|
||||
).toMatchScreenshot("invite-room-tree-with-avatar.png");
|
||||
});
|
||||
|
||||
test("should be able to hide media in rooms globally", async ({ page, app, room, user }) => {
|
||||
const settings = await app.settings.openUserSettings("Preferences");
|
||||
await settings.getByLabel("Show media in timeline").getByRole("radio", { name: "Always hide" }).click();
|
||||
await app.closeDialog();
|
||||
await app.viewRoomById(room.roomId);
|
||||
await page.getByRole("button", { name: "Accept" }).click();
|
||||
await expect(page.getByText("Show image")).toBeVisible();
|
||||
});
|
||||
test("should be able to hide media in non-private rooms globally", async ({ page, app, room, user, bot }) => {
|
||||
await bot.sendStateEvent(room.roomId, "m.room.join_rules", {
|
||||
join_rule: "public",
|
||||
});
|
||||
const settings = await app.settings.openUserSettings("Preferences");
|
||||
await settings.getByLabel("Show media in timeline").getByLabel("In private rooms").click();
|
||||
await app.closeDialog();
|
||||
await app.viewRoomById(room.roomId);
|
||||
await page.getByRole("button", { name: "Accept" }).click();
|
||||
await expect(page.getByText("Show image")).toBeVisible();
|
||||
for (const joinRule of ["invite", "knock", "restricted"] as RoomJoinRulesEventContent["join_rule"][]) {
|
||||
await bot.sendStateEvent(room.roomId, "m.room.join_rules", {
|
||||
join_rule: joinRule,
|
||||
} satisfies RoomJoinRulesEventContent);
|
||||
await expect(page.getByText("Show image")).not.toBeVisible();
|
||||
}
|
||||
});
|
||||
test("should be able to show media in rooms globally", async ({ page, app, room, user }) => {
|
||||
const settings = await app.settings.openUserSettings("Preferences");
|
||||
await settings.getByLabel("Show media in timeline").getByRole("radio", { name: "Always show" }).click();
|
||||
await app.closeDialog();
|
||||
await app.viewRoomById(room.roomId);
|
||||
await page.getByRole("button", { name: "Accept" }).click();
|
||||
await expect(page.getByText("Show image")).not.toBeVisible();
|
||||
});
|
||||
test("should be able to hide media in an individual room", async ({ page, app, room, user }) => {
|
||||
const settings = await app.settings.openUserSettings("Preferences");
|
||||
await settings.getByLabel("Show media in timeline").getByRole("radio", { name: "Always show" }).click();
|
||||
await app.closeDialog();
|
||||
|
||||
await app.viewRoomById(room.roomId);
|
||||
await page.getByRole("button", { name: "Accept" }).click();
|
||||
|
||||
const roomSettings = await app.settings.openRoomSettings("General");
|
||||
await roomSettings.getByLabel("Show media in timeline").getByRole("radio", { name: "Always hide" }).click();
|
||||
await app.closeDialog();
|
||||
|
||||
await expect(page.getByText("Show image")).toBeVisible();
|
||||
});
|
||||
test("should be able to show media in an individual room", async ({ page, app, room, user }) => {
|
||||
const settings = await app.settings.openUserSettings("Preferences");
|
||||
await settings.getByLabel("Show media in timeline").getByRole("radio", { name: "Always hide" }).click();
|
||||
await app.closeDialog();
|
||||
|
||||
await app.viewRoomById(room.roomId);
|
||||
await page.getByRole("button", { name: "Accept" }).click();
|
||||
|
||||
const roomSettings = await app.settings.openRoomSettings("General");
|
||||
await roomSettings.getByLabel("Show media in timeline").getByRole("radio", { name: "Always show" }).click();
|
||||
await app.closeDialog();
|
||||
|
||||
await expect(page.getByText("Show image")).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
@@ -6,19 +6,11 @@ Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
export default class ExampleModule {
|
||||
static moduleApiVersion = "^1.0.0";
|
||||
static moduleApiVersion = "^0.1.0";
|
||||
constructor(api) {
|
||||
this.api = api;
|
||||
|
||||
this.api.i18n.register({
|
||||
key: {
|
||||
en: "%(brand)s module loading successful!",
|
||||
de: "%(brand)s-Module erfolgreich geladen!",
|
||||
},
|
||||
});
|
||||
}
|
||||
async load() {
|
||||
const brand = this.api.config.get("brand");
|
||||
alert(this.api.i18n.translate("key", { brand }));
|
||||
alert("Testing module loading successful!");
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 956 KiB After Width: | Height: | Size: 957 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 982 KiB After Width: | Height: | Size: 985 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 106 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 64 KiB |