Compare commits
110 Commits
v1.11.88-r
...
t3chguy/ca
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d82dc2e06 | ||
|
|
effef7eaa7 | ||
|
|
9826a8851d | ||
|
|
ebef0d353e | ||
|
|
f1899b9eb1 | ||
|
|
027891a35a | ||
|
|
2f7c28ded0 | ||
|
|
b6aba1477b | ||
|
|
056ecbb138 | ||
|
|
7685e547de | ||
|
|
a0a4211447 | ||
|
|
0ad4e13e2d | ||
|
|
f406305510 | ||
|
|
6cb174e3d9 | ||
|
|
c569478240 | ||
|
|
2bd8e049c7 | ||
|
|
e8d69dc592 | ||
|
|
50ac509a01 | ||
|
|
3e27a0019d | ||
|
|
5caad70191 | ||
|
|
6846679d34 | ||
|
|
7e5420100a | ||
|
|
f75d1f5a5e | ||
|
|
66bbb84e56 | ||
|
|
48152d2cd1 | ||
|
|
0d50e34763 | ||
|
|
f157c90ba9 | ||
|
|
cccb847d4e | ||
|
|
a5b739c45a | ||
|
|
9b1e165e6c | ||
|
|
0e2b16abf1 | ||
|
|
f6e999c87d | ||
|
|
3983bd5646 | ||
|
|
e8402f1657 | ||
|
|
69237e7df2 | ||
|
|
cf1c0805f1 | ||
|
|
b6a1aea825 | ||
|
|
b97005c182 | ||
|
|
9599c57a20 | ||
|
|
afc0dd5f86 | ||
|
|
7acadc29cc | ||
|
|
a73faffe37 | ||
|
|
1e758cacae | ||
|
|
7b565e7997 | ||
|
|
b16088d098 | ||
|
|
29624f7bcb | ||
|
|
d8f6c12c3d | ||
|
|
69ee8fd96a | ||
|
|
3fb10baedf | ||
|
|
cf49f9e22c | ||
|
|
703149d76d | ||
|
|
bd3e93e8dd | ||
|
|
0555701829 | ||
|
|
417db4c9b2 | ||
|
|
4e151f8d03 | ||
|
|
afa7ec695d | ||
|
|
e98529824e | ||
|
|
16d2cccb73 | ||
|
|
9d5141cfaa | ||
|
|
1e42f28a69 | ||
|
|
4e1bd69e4d | ||
|
|
12943954c6 | ||
|
|
ec95435724 | ||
|
|
7e1927d388 | ||
|
|
0b24d33c64 | ||
|
|
db02f26005 | ||
|
|
b07d10cb23 | ||
|
|
179b17434e | ||
|
|
ab401160f8 | ||
|
|
5448de5dd6 | ||
|
|
be181d2c79 | ||
|
|
baaed75c4b | ||
|
|
cd7cf86b96 | ||
|
|
2c4a079153 | ||
|
|
9099338af8 | ||
|
|
f621c342ff | ||
|
|
4c1924311f | ||
|
|
a7e3764c27 | ||
|
|
07f1680ba0 | ||
|
|
3fbc9e6de6 | ||
|
|
117bee787f | ||
|
|
580213da5d | ||
|
|
22530d6ea5 | ||
|
|
e7d9df24e2 | ||
|
|
95c879c9e5 | ||
|
|
cbc1838755 | ||
|
|
c2799a1812 | ||
|
|
980b922348 | ||
|
|
ad77f7943b | ||
|
|
89d7dca464 | ||
|
|
aa44cadb02 | ||
|
|
941f4e1005 | ||
|
|
9b85c2d0fd | ||
|
|
1e0dfd0241 | ||
|
|
bea1b8eb85 | ||
|
|
d5db16ca24 | ||
|
|
edaf9773c0 | ||
|
|
7ea188cf89 | ||
|
|
a581e776a8 | ||
|
|
8d261d9819 | ||
|
|
299270e52d | ||
|
|
943b817194 | ||
|
|
2aa72bb40b | ||
|
|
a755e399cf | ||
|
|
8dff758153 | ||
|
|
cf3bdbdc7a | ||
|
|
ba98c2085d | ||
|
|
b330de5d6e | ||
|
|
b86bb5cc2f | ||
|
|
e835cab139 |
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
@@ -15,5 +15,5 @@
|
|||||||
/src/i18n/strings
|
/src/i18n/strings
|
||||||
/src/i18n/strings/en_EN.json @element-hq/element-web-reviewers
|
/src/i18n/strings/en_EN.json @element-hq/element-web-reviewers
|
||||||
# Ignore the synapse plugin as this is updated by GHA for docker image updating
|
# Ignore the synapse plugin as this is updated by GHA for docker image updating
|
||||||
/playwright/plugins/homeserver/synapse/index.ts
|
/playwright/testcontainers/synapse.ts
|
||||||
|
|
||||||
|
|||||||
3
.github/labels.yml
vendored
3
.github/labels.yml
vendored
@@ -210,6 +210,9 @@
|
|||||||
- name: "X-Upcoming-Release-Blocker"
|
- name: "X-Upcoming-Release-Blocker"
|
||||||
description: "This does not affect the current release cycle but will affect the next one"
|
description: "This does not affect the current release cycle but will affect the next one"
|
||||||
color: "e99695"
|
color: "e99695"
|
||||||
|
- name: "X-Run-All-Tests"
|
||||||
|
description: "When applied to PRs, it'll run the full gamut of end-to-end tests on the PR"
|
||||||
|
color: "ff7979"
|
||||||
- name: "Z-Actions"
|
- name: "Z-Actions"
|
||||||
color: "ededed"
|
color: "ededed"
|
||||||
- name: "Z-Cache-Confusion"
|
- name: "Z-Cache-Confusion"
|
||||||
|
|||||||
35
.github/workflows/build.yml
vendored
35
.github/workflows/build.yml
vendored
@@ -5,6 +5,9 @@ on:
|
|||||||
branches: [develop, master]
|
branches: [develop, master]
|
||||||
merge_group:
|
merge_group:
|
||||||
types: [checks_requested]
|
types: [checks_requested]
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.head_ref || github.sha }}
|
||||||
|
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||||
# develop pushes and repository_dispatch handled in build_develop.yaml
|
# develop pushes and repository_dispatch handled in build_develop.yaml
|
||||||
env:
|
env:
|
||||||
# These must be set for fetchdep.sh to get the right branch
|
# These must be set for fetchdep.sh to get the right branch
|
||||||
@@ -37,14 +40,38 @@ jobs:
|
|||||||
|
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
cache: "yarn"
|
# Disable cache on Windows as it is slower than not caching
|
||||||
|
# https://github.com/actions/setup-node/issues/975
|
||||||
|
cache: ${{ runner.os != 'Windows' && 'yarn' || '' }}
|
||||||
node-version: "lts/*"
|
node-version: "lts/*"
|
||||||
|
|
||||||
# Workaround for yarn install timeouts, especially on Windows
|
# Workaround for yarn install timeouts, especially on Windows
|
||||||
- run: yarn config set network-timeout 300000
|
- run: yarn config set network-timeout 300000
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Fetch layered build
|
||||||
run: "./scripts/layered.sh"
|
id: layered_build
|
||||||
|
env:
|
||||||
|
# tell layered.sh to check out the right sha of the JS-SDK & EW, if they were given one
|
||||||
|
JS_SDK_GITHUB_BASE_REF: ${{ inputs.matrix-js-sdk-sha }}
|
||||||
|
run: |
|
||||||
|
scripts/layered.sh
|
||||||
|
JSSDK_SHA=$(git -C matrix-js-sdk rev-parse --short=12 HEAD)
|
||||||
|
VECTOR_SHA=$(git rev-parse --short=12 HEAD)
|
||||||
|
echo "VERSION=$VECTOR_SHA--js-$JSSDK_SHA" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Copy config
|
||||||
|
run: cp element.io/develop/config.json config.json
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: "yarn build"
|
env:
|
||||||
|
CI_PACKAGE: true
|
||||||
|
VERSION: "${{ steps.layered_build.outputs.VERSION }}"
|
||||||
|
run: |
|
||||||
|
yarn build
|
||||||
|
|
||||||
|
- name: Upload Artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: webapp-${{ matrix.image }}
|
||||||
|
path: webapp
|
||||||
|
retention-days: 1
|
||||||
|
|||||||
6
.github/workflows/deploy.yml
vendored
6
.github/workflows/deploy.yml
vendored
@@ -16,6 +16,11 @@ on:
|
|||||||
options:
|
options:
|
||||||
- staging.element.io
|
- staging.element.io
|
||||||
- app.element.io
|
- app.element.io
|
||||||
|
skip-checks:
|
||||||
|
description: Skip CI on the tagged commit
|
||||||
|
required: true
|
||||||
|
default: false
|
||||||
|
type: boolean
|
||||||
concurrency: ${{ inputs.site || 'staging.element.io' }}
|
concurrency: ${{ inputs.site || 'staging.element.io' }}
|
||||||
permissions: {}
|
permissions: {}
|
||||||
jobs:
|
jobs:
|
||||||
@@ -75,6 +80,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Wait for other steps to succeed
|
- name: Wait for other steps to succeed
|
||||||
uses: t3chguy/wait-on-check-action@18541021811b56544d90e0f073401c2b99e249d6 # fork
|
uses: t3chguy/wait-on-check-action@18541021811b56544d90e0f073401c2b99e249d6 # fork
|
||||||
|
if: inputs.skip-checks != true
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.sha }}
|
ref: ${{ github.sha }}
|
||||||
running-workflow-name: "Deploy to Cloudflare Pages"
|
running-workflow-name: "Deploy to Cloudflare Pages"
|
||||||
|
|||||||
6
.github/workflows/dockerhub.yaml
vendored
6
.github/workflows/dockerhub.yaml
vendored
@@ -24,10 +24,10 @@ jobs:
|
|||||||
uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3
|
uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3
|
uses: docker/setup-qemu-action@53851d14592bedcffcf25ea515637cff71ef929a # v3
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # v3
|
uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3
|
||||||
with:
|
with:
|
||||||
install: true
|
install: true
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
id: build-and-push
|
id: build-and-push
|
||||||
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6
|
uses: docker/build-push-action@b32b51a8eda65d6793cd0494a773d4f6bcef32dc # v6
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: true
|
push: true
|
||||||
|
|||||||
66
.github/workflows/end-to-end-tests.yaml
vendored
66
.github/workflows/end-to-end-tests.yaml
vendored
@@ -3,6 +3,9 @@
|
|||||||
# as an artifact and run end-to-end tests.
|
# as an artifact and run end-to-end tests.
|
||||||
name: End to End Tests
|
name: End to End Tests
|
||||||
on:
|
on:
|
||||||
|
# CRON to run all Projects at 6am UTC
|
||||||
|
schedule:
|
||||||
|
- cron: "0 6 * * *"
|
||||||
pull_request: {}
|
pull_request: {}
|
||||||
merge_group:
|
merge_group:
|
||||||
types: [checks_requested]
|
types: [checks_requested]
|
||||||
@@ -32,6 +35,8 @@ concurrency:
|
|||||||
env:
|
env:
|
||||||
# fetchdep.sh needs to know our PR number
|
# fetchdep.sh needs to know our PR number
|
||||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||||
|
# Use 6 runners in the default case, but 4 when running on a schedule where we run all 5 projects (20 runners total)
|
||||||
|
NUM_RUNNERS: ${{ github.event_name == 'schedule' && 4 || 6 }}
|
||||||
|
|
||||||
permissions: {} # No permissions required
|
permissions: {} # No permissions required
|
||||||
|
|
||||||
@@ -40,6 +45,10 @@ jobs:
|
|||||||
name: "Build Element-Web"
|
name: "Build Element-Web"
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
if: inputs.skip != true
|
if: inputs.skip != true
|
||||||
|
outputs:
|
||||||
|
num-runners: ${{ env.NUM_RUNNERS }}
|
||||||
|
runners-matrix: ${{ steps.runner-vars.outputs.matrix }}
|
||||||
|
docker-cache-key: ${{ steps.runner-vars.outputs.docker-cache-key }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -72,6 +81,12 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
yarn build
|
yarn build
|
||||||
|
|
||||||
|
# Heuristic for calculating a cache key which is based on all images we pass to testcontainers
|
||||||
|
- name: Calculate docker cache key
|
||||||
|
run: |
|
||||||
|
grep -hr "Container(\"" --exclude-dir=snapshots --exclude-dir=sample-files playwright >> _docker_cache_key
|
||||||
|
grep -hr -C1 "super(" playwright/testcontainers/ >> _docker_cache_key
|
||||||
|
|
||||||
- name: Upload Artifact
|
- name: Upload Artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
@@ -79,8 +94,20 @@ jobs:
|
|||||||
path: webapp
|
path: webapp
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
|
|
||||||
|
- name: Calculate runner variables
|
||||||
|
id: runner-vars
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
env:
|
||||||
|
DOCKER_CACHE_KEY: ${{ hashFiles('_docker_cache_key') }}
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const numRunners = parseInt(process.env.NUM_RUNNERS, 10);
|
||||||
|
const matrix = Array.from({ length: numRunners }, (_, i) => i + 1);
|
||||||
|
core.setOutput("matrix", JSON.stringify(matrix));
|
||||||
|
core.setOutput("docker-cache-key", process.env.DOCKER_CACHE_KEY);
|
||||||
|
|
||||||
playwright:
|
playwright:
|
||||||
name: "Run Tests ${{ matrix.runner }}/${{ strategy.job-total }}"
|
name: "Run Tests [${{ matrix.project }}] ${{ matrix.runner }}/${{ needs.build.outputs.num-runners }}"
|
||||||
needs: build
|
needs: build
|
||||||
if: inputs.skip != true
|
if: inputs.skip != true
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
@@ -92,7 +119,19 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
# Run multiple instances in parallel to speed up the tests
|
# Run multiple instances in parallel to speed up the tests
|
||||||
runner: [1, 2, 3, 4, 5, 6]
|
runner: ${{ fromJSON(needs.build.outputs.runners-matrix) }}
|
||||||
|
project:
|
||||||
|
- Chrome
|
||||||
|
- Firefox
|
||||||
|
- WebKit
|
||||||
|
runAllTests:
|
||||||
|
- ${{ github.event_name == 'schedule' || contains(github.event.pull_request.labels.*.name, 'X-Run-All-Tests') }}
|
||||||
|
# Skip the Firefox & Safari runs unless this was a cron trigger or PR has X-Run-All-Tests label
|
||||||
|
exclude:
|
||||||
|
- runAllTests: false
|
||||||
|
project: Firefox
|
||||||
|
- runAllTests: false
|
||||||
|
project: WebKit
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
@@ -124,24 +163,35 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.cache/ms-playwright
|
~/.cache/ms-playwright
|
||||||
key: ${{ runner.os }}-playwright-${{ steps.playwright.outputs.version }}-chromium
|
key: ${{ runner.os }}-playwright-${{ steps.playwright.outputs.version }}
|
||||||
|
|
||||||
- name: Install Playwright browser
|
- name: Install Playwright browsers
|
||||||
if: steps.playwright-cache.outputs.cache-hit != 'true'
|
if: steps.playwright-cache.outputs.cache-hit != 'true'
|
||||||
run: yarn playwright install --with-deps --no-shell chromium
|
run: yarn playwright install --with-deps --no-shell
|
||||||
|
|
||||||
|
- name: Install system dependencies for WebKit
|
||||||
|
# Some WebKit dependencies seem to lay outside the cache and will need to be installed separately
|
||||||
|
if: matrix.project == 'WebKit' && steps.playwright-cache.outputs.cache-hit == 'true'
|
||||||
|
run: yarn playwright install-deps webkit
|
||||||
|
|
||||||
|
- name: Docker image cache
|
||||||
|
uses: ScribeMD/docker-cache@fb28c93772363301b8d0a6072ce850224b73f74e # 0.5.0
|
||||||
|
with:
|
||||||
|
key: ${{ needs.build.outputs.docker-cache-key }}
|
||||||
|
|
||||||
# We skip tests tagged with @mergequeue when running on PRs, but run them in MQ and everywhere else
|
# We skip tests tagged with @mergequeue when running on PRs, but run them in MQ and everywhere else
|
||||||
- name: Run Playwright tests
|
- name: Run Playwright tests
|
||||||
run: |
|
run: |
|
||||||
yarn playwright test \
|
yarn playwright test \
|
||||||
--shard "${{ matrix.runner }}/${{ strategy.job-total }}" \
|
--shard "${{ matrix.runner }}/${{ needs.build.outputs.num-runners }}" \
|
||||||
${{ github.event_name == 'pull_request' && '--grep-invert @mergequeue' || '' }}
|
--project="${{ matrix.project }}" \
|
||||||
|
${{ (github.event_name == 'pull_request' && matrix.runAllTests == false ) && '--grep-invert @mergequeue' || '' }}
|
||||||
|
|
||||||
- name: Upload blob report to GitHub Actions Artifacts
|
- name: Upload blob report to GitHub Actions Artifacts
|
||||||
if: always()
|
if: always()
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: all-blob-reports-${{ matrix.runner }}
|
name: all-blob-reports-${{ matrix.project }}-${{ matrix.runner }}
|
||||||
path: blob-report
|
path: blob-report
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
|
|
||||||
|
|||||||
3
.github/workflows/localazy_download.yaml
vendored
3
.github/workflows/localazy_download.yaml
vendored
@@ -3,7 +3,8 @@ on:
|
|||||||
workflow_dispatch: {}
|
workflow_dispatch: {}
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "0 6 * * 1,3,5" # Every Monday, Wednesday and Friday at 6am UTC
|
- cron: "0 6 * * 1,3,5" # Every Monday, Wednesday and Friday at 6am UTC
|
||||||
permissions: {} # We use ELEMENT_BOT_TOKEN instead
|
permissions:
|
||||||
|
pull-requests: write # needed to auto-approve PRs
|
||||||
jobs:
|
jobs:
|
||||||
download:
|
download:
|
||||||
uses: matrix-org/matrix-web-i18n/.github/workflows/localazy_download.yaml@main
|
uses: matrix-org/matrix-web-i18n/.github/workflows/localazy_download.yaml@main
|
||||||
|
|||||||
4
.github/workflows/netlify.yaml
vendored
4
.github/workflows/netlify.yaml
vendored
@@ -3,7 +3,7 @@
|
|||||||
name: Upload Preview Build to Netlify
|
name: Upload Preview Build to Netlify
|
||||||
on:
|
on:
|
||||||
workflow_run:
|
workflow_run:
|
||||||
workflows: ["End to End Tests"]
|
workflows: ["Build"]
|
||||||
types:
|
types:
|
||||||
- completed
|
- completed
|
||||||
jobs:
|
jobs:
|
||||||
@@ -32,7 +32,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run-id: ${{ github.event.workflow_run.id }}
|
run-id: ${{ github.event.workflow_run.id }}
|
||||||
name: webapp
|
name: webapp-ubuntu-24.04
|
||||||
path: webapp
|
path: webapp
|
||||||
|
|
||||||
- name: 📤 Deploy to Netlify
|
- name: 📤 Deploy to Netlify
|
||||||
|
|||||||
@@ -17,13 +17,13 @@ jobs:
|
|||||||
docker pull "$IMAGE"
|
docker pull "$IMAGE"
|
||||||
INSPECT=$(docker inspect --format='{{index .RepoDigests 0}}' "$IMAGE")
|
INSPECT=$(docker inspect --format='{{index .RepoDigests 0}}' "$IMAGE")
|
||||||
DIGEST=${INSPECT#*@}
|
DIGEST=${INSPECT#*@}
|
||||||
sed -i "s/const DOCKER_TAG.*/const DOCKER_TAG = \"develop@$DIGEST\";/" playwright/plugins/homeserver/synapse/index.ts
|
sed -i "s,`$IMAGE.*`,`$IMAGE@$DIGEST`," playwright/testcontainers/synapse.ts
|
||||||
env:
|
env:
|
||||||
IMAGE: ghcr.io/element-hq/synapse:develop
|
IMAGE: ghcr.io/element-hq/synapse:develop
|
||||||
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
id: cpr
|
id: cpr
|
||||||
uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7
|
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
branch: actions/playwright-image-updates
|
branch: actions/playwright-image-updates
|
||||||
|
|||||||
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@@ -104,7 +104,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Skip SonarCloud in merge queue
|
- name: Skip SonarCloud in merge queue
|
||||||
if: github.event_name == 'merge_group' || inputs.disable_coverage == 'true'
|
if: github.event_name == 'merge_group' || inputs.disable_coverage == 'true'
|
||||||
uses: guibranco/github-status-action-v2@66088c44e212a906c32a047529a213d81809ec1c
|
uses: guibranco/github-status-action-v2@56cd38caf0615dd03f49d42ed301f1469911ac61
|
||||||
with:
|
with:
|
||||||
authToken: ${{ secrets.GITHUB_TOKEN }}
|
authToken: ${{ secrets.GITHUB_TOKEN }}
|
||||||
state: success
|
state: success
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
name: Close stale flaky issues
|
name: Close stale flaky issues
|
||||||
on:
|
on:
|
||||||
|
workflow_dispatch: {}
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "30 1 * * *"
|
- cron: "30 1 * * *"
|
||||||
permissions: {}
|
permissions: {}
|
||||||
@@ -17,3 +18,4 @@ jobs:
|
|||||||
days-before-close: 0
|
days-before-close: 0
|
||||||
close-issue-message: "This flaky test issue has not been updated in 14 days. It is being closed as presumed resolved."
|
close-issue-message: "This flaky test issue has not been updated in 14 days. It is being closed as presumed resolved."
|
||||||
exempt-issue-labels: "Z-Flaky-Test-Disabled"
|
exempt-issue-labels: "Z-Flaky-Test-Disabled"
|
||||||
|
operations-per-run: 100
|
||||||
|
|||||||
2
.github/workflows/update-jitsi.yml
vendored
2
.github/workflows/update-jitsi.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
|||||||
run: "yarn update:jitsi"
|
run: "yarn update:jitsi"
|
||||||
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7
|
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
branch: actions/jitsi-update
|
branch: actions/jitsi-update
|
||||||
|
|||||||
35
CHANGELOG.md
35
CHANGELOG.md
@@ -1,3 +1,38 @@
|
|||||||
|
Changes in [1.11.89](https://github.com/element-hq/element-web/releases/tag/v1.11.89) (2024-12-18)
|
||||||
|
==================================================================================================
|
||||||
|
This is a patch release to fix a bug which could prevent loading stored crypto state from storage, and also to fix URL previews when switching back to a room.
|
||||||
|
|
||||||
|
## 🐛 Bug Fixes
|
||||||
|
|
||||||
|
* Upgrade matrix-sdk-crypto-wasm to 1.11.0 (https://github.com/matrix-org/matrix-js-sdk/pull/4593)
|
||||||
|
* Fix url preview display ([#28766](https://github.com/element-hq/element-web/pull/28766)).
|
||||||
|
|
||||||
|
|
||||||
|
Changes in [1.11.88](https://github.com/element-hq/element-web/releases/tag/v1.11.88) (2024-12-17)
|
||||||
|
==================================================================================================
|
||||||
|
## ✨ Features
|
||||||
|
|
||||||
|
* Allow trusted Element Call widget to send and receive media encryption key to-device messages ([#28316](https://github.com/element-hq/element-web/pull/28316)). Contributed by @hughns.
|
||||||
|
* increase ringing timeout from 10 seconds to 90 seconds ([#28630](https://github.com/element-hq/element-web/pull/28630)). Contributed by @fkwp.
|
||||||
|
* Add `Close` tooltip to dialog ([#28617](https://github.com/element-hq/element-web/pull/28617)). Contributed by @florianduros.
|
||||||
|
* New UX for Share dialog ([#28598](https://github.com/element-hq/element-web/pull/28598)). Contributed by @florianduros.
|
||||||
|
* Improve performance of RoomContext in RoomHeader ([#28574](https://github.com/element-hq/element-web/pull/28574)). Contributed by @t3chguy.
|
||||||
|
* Remove `Features.RustCrypto` flag ([#28582](https://github.com/element-hq/element-web/pull/28582)). Contributed by @florianduros.
|
||||||
|
* Add Modernizr warning when running in non-secure context ([#28581](https://github.com/element-hq/element-web/pull/28581)). Contributed by @t3chguy.
|
||||||
|
|
||||||
|
## 🐛 Bug Fixes
|
||||||
|
|
||||||
|
* Fix jumpy timeline when the pinned message banner is displayed ([#28654](https://github.com/element-hq/element-web/pull/28654)). Contributed by @florianduros.
|
||||||
|
* Fix font \& spaces in settings subsection ([#28631](https://github.com/element-hq/element-web/pull/28631)). Contributed by @florianduros.
|
||||||
|
* Remove manual device verification which is not supported by the new cryptography stack ([#28588](https://github.com/element-hq/element-web/pull/28588)). Contributed by @florianduros.
|
||||||
|
* Fix code block highlighting not working reliably with many code blocks ([#28613](https://github.com/element-hq/element-web/pull/28613)). Contributed by @t3chguy.
|
||||||
|
* Remove remaining reply fallbacks code ([#28610](https://github.com/element-hq/element-web/pull/28610)). Contributed by @t3chguy.
|
||||||
|
* Provide a way to activate GIFs via the keyboard for a11y ([#28611](https://github.com/element-hq/element-web/pull/28611)). Contributed by @t3chguy.
|
||||||
|
* Fix format bar position ([#28591](https://github.com/element-hq/element-web/pull/28591)). Contributed by @florianduros.
|
||||||
|
* Fix room taking long time to load ([#28579](https://github.com/element-hq/element-web/pull/28579)). Contributed by @florianduros.
|
||||||
|
* Show the correct shield status in tooltip for more conditions ([#28476](https://github.com/element-hq/element-web/pull/28476)). Contributed by @uhoreg.
|
||||||
|
|
||||||
|
|
||||||
Changes in [1.11.87](https://github.com/element-hq/element-web/releases/tag/v1.11.87) (2024-12-03)
|
Changes in [1.11.87](https://github.com/element-hq/element-web/releases/tag/v1.11.87) (2024-12-03)
|
||||||
==================================================================================================
|
==================================================================================================
|
||||||
## ✨ Features
|
## ✨ Features
|
||||||
|
|||||||
27
Dockerfile
27
Dockerfile
@@ -1,20 +1,17 @@
|
|||||||
# Builder
|
# Builder
|
||||||
FROM --platform=$BUILDPLATFORM node:22-bullseye as builder
|
FROM --platform=$BUILDPLATFORM node:22-bullseye AS builder
|
||||||
|
|
||||||
# Support custom branch of the js-sdk. This also helps us build images of element-web develop.
|
# Support custom branch of the js-sdk. This also helps us build images of element-web develop.
|
||||||
ARG USE_CUSTOM_SDKS=false
|
ARG USE_CUSTOM_SDKS=false
|
||||||
ARG JS_SDK_REPO="https://github.com/matrix-org/matrix-js-sdk.git"
|
ARG JS_SDK_REPO="https://github.com/matrix-org/matrix-js-sdk.git"
|
||||||
ARG JS_SDK_BRANCH="master"
|
ARG JS_SDK_BRANCH="master"
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y git dos2unix
|
|
||||||
|
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
|
|
||||||
COPY . /src
|
COPY . /src
|
||||||
RUN dos2unix /src/scripts/docker-link-repos.sh && bash /src/scripts/docker-link-repos.sh
|
RUN /src/scripts/docker-link-repos.sh
|
||||||
RUN yarn --network-timeout=200000 install
|
RUN yarn --network-timeout=200000 install
|
||||||
|
RUN /src/scripts/docker-package.sh
|
||||||
RUN dos2unix /src/scripts/docker-package.sh /src/scripts/get-version-from-git.sh /src/scripts/normalize-version.sh && bash /src/scripts/docker-package.sh
|
|
||||||
|
|
||||||
# Copy the config now so that we don't create another layer in the app image
|
# Copy the config now so that we don't create another layer in the app image
|
||||||
RUN cp /src/config.sample.json /src/webapp/config.json
|
RUN cp /src/config.sample.json /src/webapp/config.json
|
||||||
@@ -24,8 +21,22 @@ FROM nginx:alpine-slim
|
|||||||
|
|
||||||
COPY --from=builder /src/webapp /app
|
COPY --from=builder /src/webapp /app
|
||||||
|
|
||||||
# Override default nginx config
|
# Override default nginx config. Templates in `/etc/nginx/templates` are passed
|
||||||
COPY /nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf
|
# through `envsubst` by the nginx docker image entry point.
|
||||||
|
COPY /docker/nginx-templates/* /etc/nginx/templates/
|
||||||
|
|
||||||
|
# Tell nginx to put its pidfile elsewhere, so it can run as non-root
|
||||||
|
RUN sed -i -e 's,/var/run/nginx.pid,/tmp/nginx.pid,' /etc/nginx/nginx.conf
|
||||||
|
|
||||||
|
# nginx user must own the cache and etc directory to write cache and tweak the nginx config
|
||||||
|
RUN chown -R nginx:0 /var/cache/nginx /etc/nginx
|
||||||
|
RUN chmod -R g+w /var/cache/nginx /etc/nginx
|
||||||
|
|
||||||
RUN rm -rf /usr/share/nginx/html \
|
RUN rm -rf /usr/share/nginx/html \
|
||||||
&& ln -s /app /usr/share/nginx/html
|
&& ln -s /app /usr/share/nginx/html
|
||||||
|
|
||||||
|
# Run as nginx user by default
|
||||||
|
USER nginx
|
||||||
|
|
||||||
|
# HTTP listen port
|
||||||
|
ENV ELEMENT_WEB_PORT=80
|
||||||
|
|||||||
6
LICENSE-COMMERCIAL
Normal file
6
LICENSE-COMMERCIAL
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
Licensees holding a valid commercial license with Element may use this
|
||||||
|
software in accordance with the terms contained in a written agreement
|
||||||
|
between you and Element.
|
||||||
|
|
||||||
|
To purchase a commercial license please contact our sales team at
|
||||||
|
licensing@element.io
|
||||||
15
README.md
15
README.md
@@ -311,3 +311,18 @@ For a developer guide, see the [translating dev doc](docs/translating-dev.md).
|
|||||||
Issues are triaged by community members and the Web App Team, following the [triage process](https://github.com/element-hq/element-meta/wiki/Triage-process).
|
Issues are triaged by community members and the Web App Team, following the [triage process](https://github.com/element-hq/element-meta/wiki/Triage-process).
|
||||||
|
|
||||||
We use [issue labels](https://github.com/element-hq/element-meta/wiki/Issue-labelling) to sort all incoming issues.
|
We use [issue labels](https://github.com/element-hq/element-meta/wiki/Issue-labelling) to sort all incoming issues.
|
||||||
|
|
||||||
|
## Copyright & License
|
||||||
|
|
||||||
|
Copyright (c) 2014-2017 OpenMarket Ltd
|
||||||
|
Copyright (c) 2017 Vector Creations Ltd
|
||||||
|
Copyright (c) 2017-2025 New Vector Ltd
|
||||||
|
|
||||||
|
This software is multi licensed by New Vector Ltd (Element). It can be used either:
|
||||||
|
|
||||||
|
(1) for free under the terms of the GNU Affero General Public License (as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version); OR
|
||||||
|
|
||||||
|
(2) for free under the terms of the GNU General Public License (as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version); OR
|
||||||
|
|
||||||
|
(3) under the terms of a paid-for Element Commercial License agreement between you and Element (the terms of which may vary depending on what you and Element have agreed to).
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed under the Licenses is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the Licenses for the specific language governing permissions and limitations under the Licenses.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"description": "A glossy Matrix collaboration client for the web.",
|
"description": "A glossy Matrix collaboration client for the web.",
|
||||||
"repository": {
|
"repository": {
|
||||||
"url": "https://github.com/element-hq/element-web",
|
"url": "https://github.com/element-hq/element-web",
|
||||||
"license": "AGPL-3.0-only OR GPL-3.0-only"
|
"license": "AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial"
|
||||||
},
|
},
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"list": "https://github.com/element-hq/element-web/issues",
|
"list": "https://github.com/element-hq/element-web/issues",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
server {
|
server {
|
||||||
listen 80;
|
listen ${ELEMENT_WEB_PORT};
|
||||||
listen [::]:80;
|
listen [::]:${ELEMENT_WEB_PORT};
|
||||||
server_name localhost;
|
server_name localhost;
|
||||||
|
|
||||||
root /usr/share/nginx/html;
|
root /usr/share/nginx/html;
|
||||||
@@ -60,6 +60,22 @@ would be:
|
|||||||
docker run --rm -p 127.0.0.1:80:80 -v /etc/element-web/config.json:/app/config.json vectorim/element-web
|
docker run --rm -p 127.0.0.1:80:80 -v /etc/element-web/config.json:/app/config.json vectorim/element-web
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The Docker image is configured to run as an unprivileged (non-root) user by
|
||||||
|
default. This should be fine on modern Docker runtimes, but binding to port 80
|
||||||
|
on other runtimes may require root privileges. To resolve this, either run the
|
||||||
|
image as root (`docker run --user 0`) or, better, change the port that nginx
|
||||||
|
listens on via the `ELEMENT_WEB_PORT` environment variable.
|
||||||
|
|
||||||
|
The behaviour of the docker image can be customised via the following
|
||||||
|
environment variables:
|
||||||
|
|
||||||
|
- `ELEMENT_WEB_PORT`
|
||||||
|
|
||||||
|
The port to listen on (within the docker container) for HTTP
|
||||||
|
traffic. Defaults to `80`.
|
||||||
|
|
||||||
|
### Building the docker image
|
||||||
|
|
||||||
To build the image yourself:
|
To build the image yourself:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
22
docs/oidc.md
22
docs/oidc.md
@@ -1,29 +1,9 @@
|
|||||||
# OIDC and delegated authentication
|
# OIDC and delegated authentication
|
||||||
|
|
||||||
## Compatibility/OIDC-aware mode
|
|
||||||
|
|
||||||
[MSC2965: OIDC provider discovery](https://github.com/matrix-org/matrix-spec-proposals/pull/2965)
|
|
||||||
[MSC3824: OIDC aware clients](https://github.com/matrix-org/matrix-spec-proposals/pull/3824)
|
|
||||||
This mode uses an SSO flow to gain a `loginToken` from the authentication provider, then continues with SSO login.
|
|
||||||
Element Web uses [MSC2965: OIDC provider discovery](https://github.com/matrix-org/matrix-spec-proposals/pull/2965) to discover the configured provider.
|
|
||||||
Wherever valid MSC2965 configuration is discovered, OIDC-aware login flow will be the only option offered.
|
|
||||||
|
|
||||||
## (🧪Experimental) OIDC-native flow
|
|
||||||
|
|
||||||
Can be enabled by a config-level-only setting in `config.json`
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"features": {
|
|
||||||
"feature_oidc_native_flow": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
See https://areweoidcyet.com/client-implementation-guide/ for implementation details.
|
See https://areweoidcyet.com/client-implementation-guide/ for implementation details.
|
||||||
|
|
||||||
Element Web uses [MSC2965: OIDC provider discovery](https://github.com/matrix-org/matrix-spec-proposals/pull/2965) to discover the configured provider.
|
Element Web uses [MSC2965: OIDC provider discovery](https://github.com/matrix-org/matrix-spec-proposals/pull/2965) to discover the configured provider.
|
||||||
Where OIDC native login flow is enabled and valid MSC2965 configuration is discovered, OIDC native login flow will be the only login option offered.
|
Where a valid MSC2965 configuration is discovered, OIDC native login flow will be the only login option offered.
|
||||||
Element Web will attempt to [dynamically register](https://openid.net/specs/openid-connect-registration-1_0.html) with the configured OP.
|
Element Web will attempt to [dynamically register](https://openid.net/specs/openid-connect-registration-1_0.html) with the configured OP.
|
||||||
Then, authentication will be completed [as described here](https://areweoidcyet.com/client-implementation-guide/).
|
Then, authentication will be completed [as described here](https://areweoidcyet.com/client-implementation-guide/).
|
||||||
|
|
||||||
|
|||||||
@@ -23,21 +23,19 @@ element-web project is fine: leave it running it a different terminal as you wou
|
|||||||
when developing. Alternatively if you followed the development set up from element-web then
|
when developing. Alternatively if you followed the development set up from element-web then
|
||||||
Playwright will be capable of running the webserver on its own if it isn't already running.
|
Playwright will be capable of running the webserver on its own if it isn't already running.
|
||||||
|
|
||||||
The tests use Docker to launch Homeserver (Synapse or Dendrite) instances to test against, so you'll also
|
The tests use [testcontainers](https://node.testcontainers.org/) to launch Homeserver (Synapse or Dendrite)
|
||||||
need to have Docker installed and working in order to run the Playwright tests.
|
instances to test against, so you'll also need to one of the
|
||||||
|
[supported container runtimes](#supporter-container-runtimes)
|
||||||
|
installed and working in order to run the Playwright tests.
|
||||||
|
|
||||||
There are a few different ways to run the tests yourself. The simplest is to run:
|
There are a few different ways to run the tests yourself. The simplest is to run:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
docker pull ghcr.io/element-hq/synapse:develop
|
|
||||||
yarn run test:playwright
|
yarn run test:playwright
|
||||||
```
|
```
|
||||||
|
|
||||||
This will run the Playwright tests once, non-interactively.
|
This will run the Playwright tests once, non-interactively.
|
||||||
|
|
||||||
Note: you don't need to run the `docker pull` command every time, but you should
|
|
||||||
do it regularly to ensure you are running against an up-to-date Synapse.
|
|
||||||
|
|
||||||
You can also run individual tests this way too, as you'd expect:
|
You can also run individual tests this way too, as you'd expect:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
@@ -53,41 +51,33 @@ yarn run test:playwright:open --headed --debug
|
|||||||
|
|
||||||
See more command line options at <https://playwright.dev/docs/test-cli>.
|
See more command line options at <https://playwright.dev/docs/test-cli>.
|
||||||
|
|
||||||
### Running with Rust cryptography
|
## Projects
|
||||||
|
|
||||||
`matrix-js-sdk` is currently in the
|
By default, Playwright will run all "Projects", this means tests will run against Chrome, Firefox and "Safari" (Webkit).
|
||||||
[process](https://github.com/vector-im/element-web/issues/21972) of being
|
We only run tests against Chrome in pull request CI, but all projects in the merge queue.
|
||||||
updated to replace its end-to-end encryption implementation to use the [Matrix
|
Some tests are excluded from running on certain browsers due to incompatibilities in the test harness.
|
||||||
Rust SDK](https://github.com/matrix-org/matrix-rust-sdk). This is not currently
|
|
||||||
enabled by default, but it is possible to have Playwright configure Element to use
|
|
||||||
the Rust crypto implementation by passing `--project="Rust Crypto"` or using
|
|
||||||
the top left options in open mode.
|
|
||||||
|
|
||||||
## How the Tests Work
|
## How the Tests Work
|
||||||
|
|
||||||
Everything Playwright-related lives in the `playwright/` subdirectory of react-sdk
|
Everything Playwright-related lives in the `playwright/` subdirectory
|
||||||
as is typical for Playwright tests. Likewise, tests live in `playwright/e2e`.
|
as is typical for Playwright tests. Likewise, tests live in `playwright/e2e`.
|
||||||
|
|
||||||
`playwright/plugins/homeservers` contains Playwright plugins that starts instances
|
`playwright/testcontainers` contains the testcontainers which start instances
|
||||||
of Synapse/Dendrite in Docker containers. These servers are what Element-web runs
|
of Synapse/Dendrite. These servers are what Element-web runs against in the tests.
|
||||||
against in the tests.
|
|
||||||
|
|
||||||
Synapse can be launched with different configurations in order to test element
|
Synapse can be launched with different configurations in order to test element
|
||||||
in different configurations. `playwright/plugins/homeserver/synapse/templates`
|
in different configurations. You can specify `synapseConfigOptions` as such:
|
||||||
contains template configuration files for each different configuration.
|
|
||||||
|
|
||||||
Each test suite can then launch whatever Synapse instances it needs in whatever
|
```typescript
|
||||||
configurations.
|
test.use({
|
||||||
|
synapseConfigOptions: {
|
||||||
|
// The config options to pass to the Synapse instance
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
Note that although tests should stop the Homeserver instances after running and the
|
The appropriate homeserver will be launched by the Playwright worker and reused for all tests which match the worker configuration.
|
||||||
plugin also stop any remaining instances after all tests have run, it is possible
|
The logs from testcontainers will be attached to any reports output from Playwright.
|
||||||
to be left with some stray containers if, for example, you terminate a test such
|
|
||||||
that the `after()` does not run and also exit Playwright uncleanly. All the containers
|
|
||||||
it starts are prefixed, so they are easy to recognise. They can be removed safely.
|
|
||||||
|
|
||||||
After each test run, logs from the Synapse instances are saved in `playwright/logs/synapse`
|
|
||||||
with each instance in a separate directory named after its ID. These logs are removed
|
|
||||||
at the start of each test run.
|
|
||||||
|
|
||||||
## Writing Tests
|
## Writing Tests
|
||||||
|
|
||||||
@@ -117,25 +107,6 @@ Homeserver instances should be reasonably cheap to start (you may see the first
|
|||||||
while as it pulls the Docker image).
|
while as it pulls the Docker image).
|
||||||
You do not need to explicitly clean up the instance as it will be cleaned up by the fixture.
|
You do not need to explicitly clean up the instance as it will be cleaned up by the fixture.
|
||||||
|
|
||||||
### Synapse Config Templates
|
|
||||||
|
|
||||||
When a Synapse instance is started, it's given a config generated from one of the config
|
|
||||||
templates in `playwright/plugins/homeserver/synapse/templates`. There are a couple of special files
|
|
||||||
in these templates:
|
|
||||||
|
|
||||||
- `homeserver.yaml`:
|
|
||||||
Template substitution happens in this file. Template variables are:
|
|
||||||
- `REGISTRATION_SECRET`: The secret used to register users via the REST API.
|
|
||||||
- `MACAROON_SECRET_KEY`: Generated each time for security
|
|
||||||
- `FORM_SECRET`: Generated each time for security
|
|
||||||
- `PUBLIC_BASEURL`: The localhost url + port combination the synapse is accessible at
|
|
||||||
- `localhost.signing.key`: A signing key is auto-generated and saved to this file.
|
|
||||||
Config templates should not contain a signing key and instead assume that one will exist
|
|
||||||
in this file.
|
|
||||||
|
|
||||||
All other files in the template are copied recursively to `/data/`, so the file `foo.html`
|
|
||||||
in a template can be referenced in the config as `/data/foo.html`.
|
|
||||||
|
|
||||||
### Logging In
|
### Logging In
|
||||||
|
|
||||||
We again heavily leverage the magic of [Playwright fixtures](https://playwright.dev/docs/test-fixtures).
|
We again heavily leverage the magic of [Playwright fixtures](https://playwright.dev/docs/test-fixtures).
|
||||||
@@ -224,3 +195,20 @@ We use test tags to categorise tests for running subsets more efficiently.
|
|||||||
|
|
||||||
- `@mergequeue`: Tests that are slow or flaky and cover areas of the app we update seldom, should not be run on every PR commit but will be run in the Merge Queue.
|
- `@mergequeue`: Tests that are slow or flaky and cover areas of the app we update seldom, should not be run on every PR commit but will be run in the Merge Queue.
|
||||||
- `@screenshot`: Tests that use `toMatchScreenshot` to speed up a run of `test:playwright:screenshots`. A test with this tag must not also have the `@mergequeue` tag as this would cause false positives in the stale screenshot detection.
|
- `@screenshot`: Tests that use `toMatchScreenshot` to speed up a run of `test:playwright:screenshots`. A test with this tag must not also have the `@mergequeue` tag as this would cause false positives in the stale screenshot detection.
|
||||||
|
- `@no-$project`: Tests which are unsupported in $Project. These tests will be skipped when running in $Project.
|
||||||
|
|
||||||
|
Anything testing Matrix media will need to have `@no-firefox` and `@no-webkit` as those rely on the service worker which
|
||||||
|
has to be disabled in Playwright on Firefox & Webkit to retain routing functionality.
|
||||||
|
Anything testing VoIP/microphone will need to have `@no-webkit` as fake microphone functionality is not available
|
||||||
|
there at this time.
|
||||||
|
|
||||||
|
If you wish to run all tests in a PR, you can give it the label `X-Run-All-Tests`.
|
||||||
|
|
||||||
|
## Supporter container runtimes
|
||||||
|
|
||||||
|
We use testcontainers to spin up various instances of Synapse, Matrix Authentication Service, and more.
|
||||||
|
It supports Docker out of the box but also has support for Podman, Colima, Rancher, you just need to follow some instructions to achieve it:
|
||||||
|
https://node.testcontainers.org/supported-container-runtimes/
|
||||||
|
|
||||||
|
If you are running under Colima, you may need to set the environment variable `TMPDIR` to `/tmp/colima` or a path
|
||||||
|
within `$HOME` to allow bind mounting temporary directories into the Docker containers.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -14,6 +14,8 @@ const config: Config = {
|
|||||||
testEnvironment: "jsdom",
|
testEnvironment: "jsdom",
|
||||||
testEnvironmentOptions: {
|
testEnvironmentOptions: {
|
||||||
url: "http://localhost/",
|
url: "http://localhost/",
|
||||||
|
// This is needed to be able to load dual CJS/ESM WASM packages e.g. rust crypto & matrix-wywiwyg
|
||||||
|
customExportConditions: ["browser", "node"],
|
||||||
},
|
},
|
||||||
testMatch: ["<rootDir>/test/**/*-test.[tj]s?(x)"],
|
testMatch: ["<rootDir>/test/**/*-test.[tj]s?(x)"],
|
||||||
globalSetup: "<rootDir>/test/globalSetup.ts",
|
globalSetup: "<rootDir>/test/globalSetup.ts",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2022-2024 New Vector Ltd.
|
Copyright 2022-2024 New Vector Ltd.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2022-2024 New Vector Ltd.
|
Copyright 2022-2024 New Vector Ltd.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2022-2024 New Vector Ltd.
|
Copyright 2022-2024 New Vector Ltd.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
52
package.json
52
package.json
@@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"name": "element-web",
|
"name": "element-web",
|
||||||
"version": "1.11.88-rc.0",
|
"version": "1.11.89",
|
||||||
"description": "A feature-rich client for Matrix.org",
|
"description": "A feature-rich client for Matrix.org",
|
||||||
"author": "New Vector Ltd.",
|
"author": "New Vector Ltd.",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/element-hq/element-web"
|
"url": "https://github.com/element-hq/element-web"
|
||||||
},
|
},
|
||||||
"license": "AGPL-3.0-only OR GPL-3.0-only",
|
"license": "SEE LICENSE IN README.md",
|
||||||
"files": [
|
"files": [
|
||||||
"lib",
|
"lib",
|
||||||
"res",
|
"res",
|
||||||
@@ -64,16 +64,18 @@
|
|||||||
"test:playwright:open": "yarn test:playwright --ui",
|
"test:playwright:open": "yarn test:playwright --ui",
|
||||||
"test:playwright:screenshots": "yarn test:playwright:screenshots:build && yarn test:playwright:screenshots:run",
|
"test:playwright:screenshots": "yarn test:playwright:screenshots:build && yarn test:playwright:screenshots:run",
|
||||||
"test:playwright:screenshots:build": "docker build playwright -t element-web-playwright",
|
"test:playwright:screenshots:build": "docker build playwright -t element-web-playwright",
|
||||||
"test:playwright:screenshots:run": "docker run --rm --network host -e BASE_URL -e CI -v $(pwd):/work/ -v $(node -e 'console.log(require(`path`).dirname(require.resolve(`matrix-js-sdk/package.json`)))'):/work/node_modules/matrix-js-sdk -v /var/run/docker.sock:/var/run/docker.sock -v /tmp/:/tmp/ -it element-web-playwright --grep @screenshot",
|
"test:playwright:screenshots:run": "docker run --rm --network host -e BASE_URL -e CI -v $(pwd):/work/ -v $(node -e 'console.log(require(`path`).dirname(require.resolve(`matrix-js-sdk/package.json`)))'):/work/node_modules/matrix-js-sdk -v /var/run/docker.sock:/var/run/docker.sock -v /tmp/:/tmp/ -it element-web-playwright --grep @screenshot --project=Chrome",
|
||||||
"coverage": "yarn test --coverage",
|
"coverage": "yarn test --coverage",
|
||||||
"analyse:unused-exports": "ts-node ./scripts/analyse_unused_exports.ts",
|
"analyse:unused-exports": "ts-node ./scripts/analyse_unused_exports.ts",
|
||||||
"analyse:webpack-bundles": "webpack-bundle-analyzer webpack-stats.json webapp",
|
"analyse:webpack-bundles": "webpack-bundle-analyzer webpack-stats.json webapp",
|
||||||
"update:jitsi": "curl -s https://meet.element.io/libs/external_api.min.js > ./res/jitsi_external_api.min.js"
|
"update:jitsi": "curl -s https://meet.element.io/libs/external_api.min.js > ./res/jitsi_external_api.min.js"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
|
"@types/react": "18.3.18",
|
||||||
|
"@types/react-dom": "18.3.5",
|
||||||
"oidc-client-ts": "3.1.0",
|
"oidc-client-ts": "3.1.0",
|
||||||
"jwt-decode": "4.0.0",
|
"jwt-decode": "4.0.0",
|
||||||
"caniuse-lite": "1.0.30001684",
|
"caniuse-lite": "1.0.30001690",
|
||||||
"wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0",
|
"wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0",
|
||||||
"wrap-ansi": "npm:wrap-ansi@^7.0.0"
|
"wrap-ansi": "npm:wrap-ansi@^7.0.0"
|
||||||
},
|
},
|
||||||
@@ -87,9 +89,10 @@
|
|||||||
"@matrix-org/react-sdk-module-api": "^2.4.0",
|
"@matrix-org/react-sdk-module-api": "^2.4.0",
|
||||||
"@matrix-org/spec": "^1.7.0",
|
"@matrix-org/spec": "^1.7.0",
|
||||||
"@sentry/browser": "^8.0.0",
|
"@sentry/browser": "^8.0.0",
|
||||||
"@vector-im/compound-design-tokens": "^2.0.1",
|
"@types/png-chunks-extract": "^1.0.2",
|
||||||
"@vector-im/compound-web": "^7.4.0",
|
"@vector-im/compound-design-tokens": "^2.1.0",
|
||||||
"@vector-im/matrix-wysiwyg": "2.37.13",
|
"@vector-im/compound-web": "^7.5.0",
|
||||||
|
"@vector-im/matrix-wysiwyg": "2.38.0",
|
||||||
"@zxcvbn-ts/core": "^3.0.4",
|
"@zxcvbn-ts/core": "^3.0.4",
|
||||||
"@zxcvbn-ts/language-common": "^3.0.4",
|
"@zxcvbn-ts/language-common": "^3.0.4",
|
||||||
"@zxcvbn-ts/language-en": "^3.0.2",
|
"@zxcvbn-ts/language-en": "^3.0.2",
|
||||||
@@ -121,10 +124,10 @@
|
|||||||
"linkify-string": "4.2.0",
|
"linkify-string": "4.2.0",
|
||||||
"linkifyjs": "4.2.0",
|
"linkifyjs": "4.2.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"maplibre-gl": "^4.0.0",
|
"maplibre-gl": "^5.0.0",
|
||||||
"matrix-encrypt-attachment": "^1.0.3",
|
"matrix-encrypt-attachment": "^1.0.3",
|
||||||
"matrix-events-sdk": "0.0.1",
|
"matrix-events-sdk": "0.0.1",
|
||||||
"matrix-js-sdk": "35.0.0-rc.0",
|
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
|
||||||
"matrix-widget-api": "^1.10.0",
|
"matrix-widget-api": "^1.10.0",
|
||||||
"memoize-one": "^6.0.0",
|
"memoize-one": "^6.0.0",
|
||||||
"mime": "^4.0.4",
|
"mime": "^4.0.4",
|
||||||
@@ -134,7 +137,7 @@
|
|||||||
"png-chunks-extract": "^1.0.0",
|
"png-chunks-extract": "^1.0.0",
|
||||||
"posthog-js": "1.157.2",
|
"posthog-js": "1.157.2",
|
||||||
"qrcode": "1.5.4",
|
"qrcode": "1.5.4",
|
||||||
"re-resizable": "6.10.1",
|
"re-resizable": "6.10.3",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-beautiful-dnd": "^13.1.0",
|
"react-beautiful-dnd": "^13.1.0",
|
||||||
"react-blurhash": "^0.3.0",
|
"react-blurhash": "^0.3.0",
|
||||||
@@ -143,12 +146,14 @@
|
|||||||
"react-transition-group": "^4.4.1",
|
"react-transition-group": "^4.4.1",
|
||||||
"rfc4648": "^1.4.0",
|
"rfc4648": "^1.4.0",
|
||||||
"sanitize-filename": "^1.6.3",
|
"sanitize-filename": "^1.6.3",
|
||||||
"sanitize-html": "2.13.1",
|
"sanitize-html": "2.14.0",
|
||||||
"tar-js": "^0.3.0",
|
"tar-js": "^0.3.0",
|
||||||
"temporal-polyfill": "^0.2.5",
|
"temporal-polyfill": "^0.2.5",
|
||||||
"ua-parser-js": "^1.0.2",
|
"ua-parser-js": "^1.0.2",
|
||||||
"uuid": "^11.0.0",
|
"uuid": "^11.0.0",
|
||||||
"what-input": "^5.2.10"
|
"what-input": "^5.2.10",
|
||||||
|
"@types/react-virtualized": "^9.21.30",
|
||||||
|
"react-virtualized": "^9.22.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@action-validator/cli": "^0.6.0",
|
"@action-validator/cli": "^0.6.0",
|
||||||
@@ -177,6 +182,7 @@
|
|||||||
"@sentry/webpack-plugin": "^2.7.1",
|
"@sentry/webpack-plugin": "^2.7.1",
|
||||||
"@stylistic/eslint-plugin": "^2.9.0",
|
"@stylistic/eslint-plugin": "^2.9.0",
|
||||||
"@svgr/webpack": "^8.0.0",
|
"@svgr/webpack": "^8.0.0",
|
||||||
|
"@testcontainers/postgresql": "^10.16.0",
|
||||||
"@testing-library/dom": "^10.4.0",
|
"@testing-library/dom": "^10.4.0",
|
||||||
"@testing-library/jest-dom": "^6.4.8",
|
"@testing-library/jest-dom": "^6.4.8",
|
||||||
"@testing-library/react": "^16.0.0",
|
"@testing-library/react": "^16.0.0",
|
||||||
@@ -188,7 +194,6 @@
|
|||||||
"@types/escape-html": "^1.0.1",
|
"@types/escape-html": "^1.0.1",
|
||||||
"@types/express": "^5.0.0",
|
"@types/express": "^5.0.0",
|
||||||
"@types/file-saver": "^2.0.3",
|
"@types/file-saver": "^2.0.3",
|
||||||
"@types/fs-extra": "^11.0.0",
|
|
||||||
"@types/glob-to-regexp": "^0.4.1",
|
"@types/glob-to-regexp": "^0.4.1",
|
||||||
"@types/jest": "29.5.12",
|
"@types/jest": "29.5.12",
|
||||||
"@types/jitsi-meet": "^2.0.2",
|
"@types/jitsi-meet": "^2.0.2",
|
||||||
@@ -201,17 +206,17 @@
|
|||||||
"@types/node-fetch": "^2.6.2",
|
"@types/node-fetch": "^2.6.2",
|
||||||
"@types/pako": "^2.0.0",
|
"@types/pako": "^2.0.0",
|
||||||
"@types/qrcode": "^1.3.5",
|
"@types/qrcode": "^1.3.5",
|
||||||
"@types/react": "18.3.3",
|
"@types/react": "18.3.18",
|
||||||
"@types/react-beautiful-dnd": "^13.0.0",
|
"@types/react-beautiful-dnd": "^13.0.0",
|
||||||
"@types/react-dom": "18.3.1",
|
"@types/react-dom": "18.3.5",
|
||||||
"@types/react-transition-group": "^4.4.0",
|
"@types/react-transition-group": "^4.4.0",
|
||||||
"@types/sanitize-html": "2.13.0",
|
"@types/sanitize-html": "2.13.0",
|
||||||
"@types/semver": "^7.5.8",
|
"@types/semver": "^7.5.8",
|
||||||
"@types/tar-js": "^0.3.5",
|
"@types/tar-js": "^0.3.5",
|
||||||
"@types/ua-parser-js": "^0.7.36",
|
"@types/ua-parser-js": "^0.7.36",
|
||||||
"@types/uuid": "^10.0.0",
|
"@types/uuid": "^10.0.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
"@typescript-eslint/eslint-plugin": "^8.19.0",
|
||||||
"@typescript-eslint/parser": "^8.0.0",
|
"@typescript-eslint/parser": "^8.19.0",
|
||||||
"babel-jest": "^29.0.0",
|
"babel-jest": "^29.0.0",
|
||||||
"babel-loader": "^9.0.0",
|
"babel-loader": "^9.0.0",
|
||||||
"babel-plugin-jsx-remove-data-test-id": "^3.0.0",
|
"babel-plugin-jsx-remove-data-test-id": "^3.0.0",
|
||||||
@@ -240,7 +245,6 @@
|
|||||||
"fetch-mock": "9.11.0",
|
"fetch-mock": "9.11.0",
|
||||||
"fetch-mock-jest": "^1.5.1",
|
"fetch-mock-jest": "^1.5.1",
|
||||||
"file-loader": "^6.0.0",
|
"file-loader": "^6.0.0",
|
||||||
"fs-extra": "^11.0.0",
|
|
||||||
"glob": "^11.0.0",
|
"glob": "^11.0.0",
|
||||||
"html-webpack-plugin": "^5.5.3",
|
"html-webpack-plugin": "^5.5.3",
|
||||||
"husky": "^9.0.0",
|
"husky": "^9.0.0",
|
||||||
@@ -254,12 +258,12 @@
|
|||||||
"lint-staged": "^15.0.2",
|
"lint-staged": "^15.0.2",
|
||||||
"mailhog": "^4.16.0",
|
"mailhog": "^4.16.0",
|
||||||
"matrix-web-i18n": "^3.2.1",
|
"matrix-web-i18n": "^3.2.1",
|
||||||
"mini-css-extract-plugin": "2.9.0",
|
"mini-css-extract-plugin": "2.9.2",
|
||||||
"minimist": "^1.2.6",
|
"minimist": "^1.2.6",
|
||||||
"modernizr": "^3.12.0",
|
"modernizr": "^3.12.0",
|
||||||
"node-fetch": "^2.6.7",
|
"node-fetch": "^2.6.7",
|
||||||
"playwright-core": "^1.45.1",
|
"playwright-core": "^1.45.1",
|
||||||
"postcss": "8.4.38",
|
"postcss": "8.4.46",
|
||||||
"postcss-easings": "^4.0.0",
|
"postcss-easings": "^4.0.0",
|
||||||
"postcss-hexrgba": "2.1.0",
|
"postcss-hexrgba": "2.1.0",
|
||||||
"postcss-import": "16.1.0",
|
"postcss-import": "16.1.0",
|
||||||
@@ -269,25 +273,27 @@
|
|||||||
"postcss-preset-env": "^10.0.0",
|
"postcss-preset-env": "^10.0.0",
|
||||||
"postcss-scss": "^4.0.4",
|
"postcss-scss": "^4.0.4",
|
||||||
"postcss-simple-vars": "^7.0.1",
|
"postcss-simple-vars": "^7.0.1",
|
||||||
"prettier": "3.4.1",
|
"prettier": "3.4.2",
|
||||||
"process": "^0.11.10",
|
"process": "^0.11.10",
|
||||||
"raw-loader": "^4.0.2",
|
"raw-loader": "^4.0.2",
|
||||||
"rimraf": "^6.0.0",
|
"rimraf": "^6.0.0",
|
||||||
"semver": "^7.5.2",
|
"semver": "^7.5.2",
|
||||||
"source-map-loader": "^5.0.0",
|
"source-map-loader": "^5.0.0",
|
||||||
|
"strip-ansi": "^7.1.0",
|
||||||
"stylelint": "^16.1.0",
|
"stylelint": "^16.1.0",
|
||||||
"stylelint-config-standard": "^36.0.0",
|
"stylelint-config-standard": "^36.0.0",
|
||||||
"stylelint-scss": "^6.0.0",
|
"stylelint-scss": "^6.0.0",
|
||||||
"stylelint-value-no-unknown-custom-properties": "^6.0.1",
|
"stylelint-value-no-unknown-custom-properties": "^6.0.1",
|
||||||
"terser-webpack-plugin": "^5.3.9",
|
"terser-webpack-plugin": "^5.3.9",
|
||||||
|
"testcontainers": "^10.16.0",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"ts-prune": "^0.10.3",
|
"ts-prune": "^0.10.3",
|
||||||
"typescript": "5.6.3",
|
"typescript": "5.7.2",
|
||||||
"util": "^0.12.5",
|
"util": "^0.12.5",
|
||||||
"web-streams-polyfill": "^4.0.0",
|
"web-streams-polyfill": "^4.0.0",
|
||||||
"webpack": "^5.89.0",
|
"webpack": "^5.89.0",
|
||||||
"webpack-bundle-analyzer": "^4.8.0",
|
"webpack-bundle-analyzer": "^4.8.0",
|
||||||
"webpack-cli": "^5.0.0",
|
"webpack-cli": "^6.0.0",
|
||||||
"webpack-dev-server": "^5.0.0",
|
"webpack-dev-server": "^5.0.0",
|
||||||
"webpack-version-file-plugin": "^0.5.0",
|
"webpack-version-file-plugin": "^0.5.0",
|
||||||
"yaml": "^2.3.3"
|
"yaml": "^2.3.3"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -11,16 +11,49 @@ import { defineConfig, devices } from "@playwright/test";
|
|||||||
const baseURL = process.env["BASE_URL"] ?? "http://localhost:8080";
|
const baseURL = process.env["BASE_URL"] ?? "http://localhost:8080";
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
projects: [{ name: "Chrome", use: { ...devices["Desktop Chrome"], channel: "chromium" } }],
|
projects: [
|
||||||
|
{
|
||||||
|
name: "Chrome",
|
||||||
|
use: {
|
||||||
|
...devices["Desktop Chrome"],
|
||||||
|
channel: "chromium",
|
||||||
|
permissions: ["clipboard-write", "clipboard-read", "microphone"],
|
||||||
|
launchOptions: {
|
||||||
|
args: ["--use-fake-ui-for-media-stream", "--use-fake-device-for-media-stream", "--mute-audio"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Firefox",
|
||||||
|
use: {
|
||||||
|
...devices["Desktop Firefox"],
|
||||||
|
launchOptions: {
|
||||||
|
firefoxUserPrefs: {
|
||||||
|
"permissions.default.microphone": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// This is needed to work around an issue between Playwright routes, Firefox, and Service workers
|
||||||
|
// https://github.com/microsoft/playwright/issues/33561#issuecomment-2471642120
|
||||||
|
serviceWorkers: "block",
|
||||||
|
},
|
||||||
|
ignoreSnapshots: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "WebKit",
|
||||||
|
use: {
|
||||||
|
...devices["Desktop Safari"],
|
||||||
|
// Seemingly WebKit has the same issue as Firefox in Playwright routes not working
|
||||||
|
// https://playwright.dev/docs/network#missing-network-events-and-service-workers
|
||||||
|
serviceWorkers: "block",
|
||||||
|
},
|
||||||
|
ignoreSnapshots: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
use: {
|
use: {
|
||||||
viewport: { width: 1280, height: 720 },
|
viewport: { width: 1280, height: 720 },
|
||||||
ignoreHTTPSErrors: true,
|
ignoreHTTPSErrors: true,
|
||||||
video: "retain-on-failure",
|
video: "retain-on-failure",
|
||||||
baseURL,
|
baseURL,
|
||||||
permissions: ["clipboard-write", "clipboard-read", "microphone"],
|
|
||||||
launchOptions: {
|
|
||||||
args: ["--use-fake-ui-for-media-stream", "--use-fake-device-for-media-stream", "--mute-audio"],
|
|
||||||
},
|
|
||||||
trace: "on-first-retry",
|
trace: "on-first-retry",
|
||||||
},
|
},
|
||||||
webServer: {
|
webServer: {
|
||||||
|
|||||||
2
playwright/@types/playwright-core.d.ts
vendored
2
playwright/@types/playwright-core.d.ts
vendored
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2024 The Matrix.org Foundation C.I.C.
|
Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM mcr.microsoft.com/playwright:v1.49.0-noble
|
FROM mcr.microsoft.com/playwright:v1.49.1-noble
|
||||||
|
|
||||||
WORKDIR /work
|
WORKDIR /work
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2024 The Matrix.org Foundation C.I.C.
|
Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2024 The Matrix.org Foundation C.I.C.
|
Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2024 The Matrix.org Foundation C.I.C.
|
Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -13,13 +13,8 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
import { expect, test } from "../../element-web-test";
|
import { expect, test } from "../../element-web-test";
|
||||||
|
|
||||||
test.use({
|
test.use({
|
||||||
startHomeserverOpts: "guest-enabled",
|
synapseConfigOptions: {
|
||||||
config: async ({ homeserver }, use) => {
|
allow_guest_access: true,
|
||||||
await use({
|
|
||||||
default_server_config: {
|
|
||||||
"m.homeserver": { base_url: homeserver.config.baseUrl },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2024 The Matrix.org Foundation C.I.C.
|
Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ Copyright 2024 New Vector Ltd.
|
|||||||
Copyright 2023 Suguru Hirahara
|
Copyright 2023 Suguru Hirahara
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ import { SettingLevel } from "../../../src/settings/SettingLevel";
|
|||||||
import { Layout } from "../../../src/settings/enums/Layout";
|
import { Layout } from "../../../src/settings/enums/Layout";
|
||||||
import { ElementAppPage } from "../../pages/ElementAppPage";
|
import { ElementAppPage } from "../../pages/ElementAppPage";
|
||||||
|
|
||||||
test.describe("Audio player", () => {
|
test.describe("Audio player", { tag: ["@no-firefox", "@no-webkit"] }, () => {
|
||||||
test.use({
|
test.use({
|
||||||
displayName: "Hanako",
|
displayName: "Hanako",
|
||||||
});
|
});
|
||||||
@@ -253,7 +253,6 @@ test.describe("Audio player", () => {
|
|||||||
|
|
||||||
// Find and click "Reply" button
|
// Find and click "Reply" button
|
||||||
const clickButtonReply = async () => {
|
const clickButtonReply = async () => {
|
||||||
await tile.scrollIntoViewIfNeeded();
|
|
||||||
await tile.hover();
|
await tile.hover();
|
||||||
await tile.getByRole("button", { name: "Reply", exact: true }).click();
|
await tile.getByRole("button", { name: "Reply", exact: true }).click();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2024 The Matrix.org Foundation C.I.C.
|
Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -79,9 +79,8 @@ test.describe("Composer", () => {
|
|||||||
// Enter some more text, then send the message
|
// Enter some more text, then send the message
|
||||||
await page.getByRole("textbox").pressSequentially("this is the spoiler text ");
|
await page.getByRole("textbox").pressSequentially("this is the spoiler text ");
|
||||||
await page.getByRole("button", { name: "Send message" }).click();
|
await page.getByRole("button", { name: "Send message" }).click();
|
||||||
// Check that a spoiler item has appeared in the timeline and locator the spoiler command text
|
// Check that a spoiler item has appeared in the timeline and contains the spoiler text
|
||||||
await expect(page.locator("button.mx_EventTile_spoiler")).toBeVisible();
|
await expect(page.locator("button.mx_EventTile_spoiler")).toHaveText("this is the spoiler text");
|
||||||
await expect(page.getByText("this is the spoiler text")).toBeVisible();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
91
playwright/e2e/crypto/backups-mas.spec.ts
Normal file
91
playwright/e2e/crypto/backups-mas.spec.ts
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024 New Vector Ltd.
|
||||||
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { test, expect } from "../../element-web-test";
|
||||||
|
import { registerAccountMas } from "../oidc";
|
||||||
|
import { isDendrite } from "../../plugins/homeserver/dendrite";
|
||||||
|
import { TestClientServerAPI } from "../csAPI";
|
||||||
|
import { masHomeserver } from "../../plugins/homeserver/synapse/masHomeserver.ts";
|
||||||
|
|
||||||
|
// These tests register an account with MAS because then we go through the "normal" registration flow
|
||||||
|
// and crypto gets set up. Using the 'user' fixture create a a user an synthesizes an existing login,
|
||||||
|
// which is faster but leaves us without crypto set up.
|
||||||
|
test.use(masHomeserver);
|
||||||
|
test.describe("Encryption state after registration", () => {
|
||||||
|
test.skip(isDendrite, "does not yet support MAS");
|
||||||
|
|
||||||
|
test("Key backup is enabled by default", async ({ page, mailhogClient, app }) => {
|
||||||
|
await page.goto("/#/login");
|
||||||
|
await page.getByRole("button", { name: "Continue" }).click();
|
||||||
|
await registerAccountMas(page, mailhogClient, "alice", "alice@email.com", "Pa$sW0rD!");
|
||||||
|
|
||||||
|
await app.settings.openUserSettings("Security & Privacy");
|
||||||
|
await expect(page.getByText("This session is backing up your keys.")).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("user is prompted to set up recovery", async ({ page, mailhogClient, app }) => {
|
||||||
|
await page.goto("/#/login");
|
||||||
|
await page.getByRole("button", { name: "Continue" }).click();
|
||||||
|
await registerAccountMas(page, mailhogClient, "alice", "alice@email.com", "Pa$sW0rD!");
|
||||||
|
|
||||||
|
await page.getByRole("button", { name: "Add room" }).click();
|
||||||
|
await page.getByRole("menuitem", { name: "New room" }).click();
|
||||||
|
await page.getByRole("textbox", { name: "Name" }).fill("test room");
|
||||||
|
await page.getByRole("button", { name: "Create room" }).click();
|
||||||
|
|
||||||
|
await expect(page.getByRole("heading", { name: "Set up recovery" })).toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe("Key backup reset from elsewhere", () => {
|
||||||
|
test.skip(isDendrite, "does not yet support MAS");
|
||||||
|
|
||||||
|
test("Key backup is disabled when reset from elsewhere", async ({ page, mailhogClient, request, homeserver }) => {
|
||||||
|
const testUsername = "alice";
|
||||||
|
const testPassword = "Pa$sW0rD!";
|
||||||
|
|
||||||
|
// there's a delay before keys are uploaded so the error doesn't appear immediately: use a fake
|
||||||
|
// clock so we can skip the delay
|
||||||
|
await page.clock.install();
|
||||||
|
|
||||||
|
await page.goto("/#/login");
|
||||||
|
await page.getByRole("button", { name: "Continue" }).click();
|
||||||
|
await registerAccountMas(page, mailhogClient, testUsername, "alice@email.com", testPassword);
|
||||||
|
|
||||||
|
await page.getByRole("button", { name: "Add room" }).click();
|
||||||
|
await page.getByRole("menuitem", { name: "New room" }).click();
|
||||||
|
await page.getByRole("textbox", { name: "Name" }).fill("test room");
|
||||||
|
await page.getByRole("button", { name: "Create room" }).click();
|
||||||
|
|
||||||
|
// @ts-ignore - this runs in the browser scope where mxMatrixClientPeg is a thing. Here, it is not.
|
||||||
|
const accessToken = await page.evaluate(() => mxMatrixClientPeg.get().getAccessToken());
|
||||||
|
|
||||||
|
const csAPI = new TestClientServerAPI(request, homeserver, accessToken);
|
||||||
|
|
||||||
|
const backupInfo = await csAPI.getCurrentBackupInfo();
|
||||||
|
|
||||||
|
await csAPI.deleteBackupVersion(backupInfo.version);
|
||||||
|
|
||||||
|
await page.getByRole("textbox", { name: "Send an encrypted message…" }).fill("/discardsession");
|
||||||
|
await page.getByRole("button", { name: "Send message" }).click();
|
||||||
|
|
||||||
|
await page.getByRole("textbox", { name: "Send an encrypted message…" }).fill("Message with broken key backup");
|
||||||
|
await page.getByRole("button", { name: "Send message" }).click();
|
||||||
|
|
||||||
|
// Should be the message we sent plus the room creation event
|
||||||
|
await expect(page.locator(".mx_EventTile")).toHaveCount(2);
|
||||||
|
await expect(
|
||||||
|
page.locator(".mx_RoomView_MessageList > .mx_EventTile_last .mx_EventTile_receiptSent"),
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
// Wait for it to try uploading the key
|
||||||
|
await page.clock.fastForward(20000);
|
||||||
|
|
||||||
|
await expect(page.getByRole("heading", { level: 1, name: "New Recovery Method" })).toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -23,7 +23,10 @@ test.describe("Backups", () => {
|
|||||||
displayName: "Hanako",
|
displayName: "Hanako",
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Create, delete and recreate a keys backup", async ({ page, user, app }, workerInfo) => {
|
test(
|
||||||
|
"Create, delete and recreate a keys backup",
|
||||||
|
{ tag: "@no-webkit" },
|
||||||
|
async ({ page, user, app }, workerInfo) => {
|
||||||
// Create a backup
|
// Create a backup
|
||||||
const securityTab = await app.settings.openUserSettings("Security & Privacy");
|
const securityTab = await app.settings.openUserSettings("Security & Privacy");
|
||||||
|
|
||||||
@@ -108,5 +111,6 @@ test.describe("Backups", () => {
|
|||||||
// go back to the settings to check that no backup was created (the setup button should still be there)
|
// go back to the settings to check that no backup was created (the setup button should still be there)
|
||||||
await app.settings.openUserSettings("Security & Privacy");
|
await app.settings.openUserSettings("Security & Privacy");
|
||||||
await expect(securityTab.getByRole("button", { name: "Set up", exact: true })).toBeVisible();
|
await expect(securityTab.getByRole("button", { name: "Set up", exact: true })).toBeVisible();
|
||||||
});
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -19,9 +19,9 @@ test.describe("Complete security", () => {
|
|||||||
homeserver,
|
homeserver,
|
||||||
credentials,
|
credentials,
|
||||||
}) => {
|
}) => {
|
||||||
await logIntoElement(page, homeserver, credentials);
|
await logIntoElement(page, credentials);
|
||||||
await expect(page.getByText("Welcome Jeff", { exact: true })).toBeVisible();
|
await expect(page.getByText("Welcome Jeff", { exact: true })).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
// see also "Verify device during login with SAS" in `verifiction.spec.ts`.
|
// see also "Verify device during login with SAS" in `verification.spec.ts`.
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@ test.describe("Cryptography", function () {
|
|||||||
* Verify that the `m.cross_signing.${keyType}` key is available on the account data on the server
|
* Verify that the `m.cross_signing.${keyType}` key is available on the account data on the server
|
||||||
* @param keyType
|
* @param keyType
|
||||||
*/
|
*/
|
||||||
async function verifyKey(app: ElementAppPage, keyType: string) {
|
async function verifyKey(app: ElementAppPage, keyType: "master" | "self_signing" | "user_signing") {
|
||||||
const accountData: { encrypted: Record<string, Record<string, string>> } = await app.client.evaluate(
|
const accountData: { encrypted: Record<string, Record<string, string>> } = await app.client.evaluate(
|
||||||
(cli, keyType) => cli.getAccountDataFromServer(`m.cross_signing.${keyType}`),
|
(cli, keyType) => cli.getAccountDataFromServer(`m.cross_signing.${keyType}`),
|
||||||
keyType,
|
keyType,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ test.describe("Cryptography", function () {
|
|||||||
await logOutOfElement(page, true);
|
await logOutOfElement(page, true);
|
||||||
|
|
||||||
// Log in again, and see how the message looks.
|
// Log in again, and see how the message looks.
|
||||||
await logIntoElement(page, homeserver, credentials);
|
await logIntoElement(page, credentials);
|
||||||
await app.viewRoomByName("Test room");
|
await app.viewRoomByName("Test room");
|
||||||
const lastTile = page.locator(".mx_EventTile").last();
|
const lastTile = page.locator(".mx_EventTile").last();
|
||||||
await expect(lastTile).toContainText("Historical messages are not available on this device");
|
await expect(lastTile).toContainText("Historical messages are not available on this device");
|
||||||
@@ -62,7 +62,7 @@ test.describe("Cryptography", function () {
|
|||||||
|
|
||||||
// Finally, log out again, and back in, skipping verification for now, and see what we see.
|
// Finally, log out again, and back in, skipping verification for now, and see what we see.
|
||||||
await logOutOfElement(page);
|
await logOutOfElement(page);
|
||||||
await logIntoElement(page, homeserver, credentials);
|
await logIntoElement(page, credentials);
|
||||||
await page.locator(".mx_AuthPage").getByRole("button", { name: "Skip verification for now" }).click();
|
await page.locator(".mx_AuthPage").getByRole("button", { name: "Skip verification for now" }).click();
|
||||||
await page.locator(".mx_AuthPage").getByRole("button", { name: "I'll verify later" }).click();
|
await page.locator(".mx_AuthPage").getByRole("button", { name: "I'll verify later" }).click();
|
||||||
await app.viewRoomByName("Test room");
|
await app.viewRoomByName("Test room");
|
||||||
|
|||||||
@@ -2,26 +2,34 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2024 The Matrix.org Foundation C.I.C.
|
Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Locator, type Page } from "@playwright/test";
|
import { Locator, type Page } from "@playwright/test";
|
||||||
|
|
||||||
import { test as base, expect } from "../../element-web-test";
|
import { test, expect } from "../../element-web-test";
|
||||||
import { viewRoomSummaryByName } from "../right-panel/utils";
|
import { viewRoomSummaryByName } from "../right-panel/utils";
|
||||||
import { isDendrite } from "../../plugins/homeserver/dendrite";
|
import { isDendrite } from "../../plugins/homeserver/dendrite";
|
||||||
|
|
||||||
const test = base.extend({
|
const ROOM_NAME = "Test room";
|
||||||
// eslint-disable-next-line no-empty-pattern
|
const NAME = "Alice";
|
||||||
startHomeserverOpts: async ({}, use) => {
|
|
||||||
await use("dehydration");
|
function getMemberTileByName(page: Page, name: string): Locator {
|
||||||
|
return page.locator(`.mx_MemberTileView, [title="${name}"]`);
|
||||||
|
}
|
||||||
|
|
||||||
|
test.use({
|
||||||
|
displayName: NAME,
|
||||||
|
synapseConfigOptions: {
|
||||||
|
experimental_features: {
|
||||||
|
msc2697_enabled: false,
|
||||||
|
msc3814_enabled: true,
|
||||||
},
|
},
|
||||||
config: async ({ homeserver, context }, use) => {
|
},
|
||||||
|
config: async ({ config, context }, use) => {
|
||||||
const wellKnown = {
|
const wellKnown = {
|
||||||
"m.homeserver": {
|
...config.default_server_config,
|
||||||
base_url: homeserver.config.baseUrl,
|
|
||||||
},
|
|
||||||
"org.matrix.msc3814": true,
|
"org.matrix.msc3814": true,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -29,29 +37,14 @@ const test = base.extend({
|
|||||||
await route.fulfill({ json: wellKnown });
|
await route.fulfill({ json: wellKnown });
|
||||||
});
|
});
|
||||||
|
|
||||||
await use({
|
await use(config);
|
||||||
default_server_config: wellKnown,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const ROOM_NAME = "Test room";
|
|
||||||
const NAME = "Alice";
|
|
||||||
|
|
||||||
function getMemberTileByName(page: Page, name: string): Locator {
|
|
||||||
return page.locator(`.mx_EntityTile, [title="${name}"]`);
|
|
||||||
}
|
|
||||||
|
|
||||||
test.describe("Dehydration", () => {
|
test.describe("Dehydration", () => {
|
||||||
test.skip(isDendrite, "does not yet support dehydration v2");
|
test.skip(isDendrite, "does not yet support dehydration v2");
|
||||||
|
|
||||||
test.use({
|
|
||||||
displayName: NAME,
|
|
||||||
});
|
|
||||||
|
|
||||||
test("Create dehydrated device", async ({ page, user, app }, workerInfo) => {
|
test("Create dehydrated device", async ({ page, user, app }, workerInfo) => {
|
||||||
test.skip(workerInfo.project.name === "Legacy Crypto", "This test only works with Rust crypto.");
|
|
||||||
|
|
||||||
// Create a backup (which will create SSSS, and dehydrated device)
|
// Create a backup (which will create SSSS, and dehydrated device)
|
||||||
|
|
||||||
const securityTab = await app.settings.openUserSettings("Security & Privacy");
|
const securityTab = await app.settings.openUserSettings("Security & Privacy");
|
||||||
@@ -95,7 +88,7 @@ test.describe("Dehydration", () => {
|
|||||||
await viewRoomSummaryByName(page, app, ROOM_NAME);
|
await viewRoomSummaryByName(page, app, ROOM_NAME);
|
||||||
|
|
||||||
await page.locator(".mx_RightPanel").getByRole("menuitem", { name: "People" }).click();
|
await page.locator(".mx_RightPanel").getByRole("menuitem", { name: "People" }).click();
|
||||||
await expect(page.locator(".mx_MemberList")).toBeVisible();
|
await expect(page.locator(".mx_MemberListView")).toBeVisible();
|
||||||
|
|
||||||
await getMemberTileByName(page, NAME).click();
|
await getMemberTileByName(page, NAME).click();
|
||||||
await page.locator(".mx_UserInfo_devices .mx_UserInfo_expand").click();
|
await page.locator(".mx_UserInfo_devices .mx_UserInfo_expand").click();
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ import {
|
|||||||
} from "./utils";
|
} from "./utils";
|
||||||
import { Bot } from "../../pages/bot";
|
import { Bot } from "../../pages/bot";
|
||||||
|
|
||||||
test.describe("Device verification", () => {
|
test.describe("Device verification", { tag: "@no-webkit" }, () => {
|
||||||
let aliceBotClient: Bot;
|
let aliceBotClient: Bot;
|
||||||
|
|
||||||
/** The backup version that was set up by the bot client. */
|
/** The backup version that was set up by the bot client. */
|
||||||
@@ -66,7 +66,7 @@ test.describe("Device verification", () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test("Verify device with SAS during login", async ({ page, app, credentials, homeserver }) => {
|
test("Verify device with SAS during login", async ({ page, app, credentials, homeserver }) => {
|
||||||
await logIntoElement(page, homeserver, credentials);
|
await logIntoElement(page, credentials);
|
||||||
|
|
||||||
// Launch the verification request between alice and the bot
|
// Launch the verification request between alice and the bot
|
||||||
const verificationRequest = await initiateAliceVerificationRequest(page);
|
const verificationRequest = await initiateAliceVerificationRequest(page);
|
||||||
@@ -93,7 +93,7 @@ test.describe("Device verification", () => {
|
|||||||
|
|
||||||
test("Verify device with QR code during login", async ({ page, app, credentials, homeserver }) => {
|
test("Verify device with QR code during login", async ({ page, app, credentials, homeserver }) => {
|
||||||
// A mode 0x02 verification: "self-verifying in which the current device does not yet trust the master key"
|
// A mode 0x02 verification: "self-verifying in which the current device does not yet trust the master key"
|
||||||
await logIntoElement(page, homeserver, credentials);
|
await logIntoElement(page, credentials);
|
||||||
|
|
||||||
// Launch the verification request between alice and the bot
|
// Launch the verification request between alice and the bot
|
||||||
const verificationRequest = await initiateAliceVerificationRequest(page);
|
const verificationRequest = await initiateAliceVerificationRequest(page);
|
||||||
@@ -137,7 +137,7 @@ test.describe("Device verification", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("Verify device with Security Phrase during login", async ({ page, app, credentials, homeserver }) => {
|
test("Verify device with Security Phrase during login", async ({ page, app, credentials, homeserver }) => {
|
||||||
await logIntoElement(page, homeserver, credentials);
|
await logIntoElement(page, credentials);
|
||||||
|
|
||||||
// Select the security phrase
|
// Select the security phrase
|
||||||
await page.locator(".mx_AuthPage").getByRole("button", { name: "Verify with Security Key or Phrase" }).click();
|
await page.locator(".mx_AuthPage").getByRole("button", { name: "Verify with Security Key or Phrase" }).click();
|
||||||
@@ -158,7 +158,7 @@ test.describe("Device verification", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("Verify device with Security Key during login", async ({ page, app, credentials, homeserver }) => {
|
test("Verify device with Security Key during login", async ({ page, app, credentials, homeserver }) => {
|
||||||
await logIntoElement(page, homeserver, credentials);
|
await logIntoElement(page, credentials);
|
||||||
|
|
||||||
// Select the security phrase
|
// Select the security phrase
|
||||||
await page.locator(".mx_AuthPage").getByRole("button", { name: "Verify with Security Key or Phrase" }).click();
|
await page.locator(".mx_AuthPage").getByRole("button", { name: "Verify with Security Key or Phrase" }).click();
|
||||||
@@ -181,7 +181,7 @@ test.describe("Device verification", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("Handle incoming verification request with SAS", async ({ page, credentials, homeserver, toasts }) => {
|
test("Handle incoming verification request with SAS", async ({ page, credentials, homeserver, toasts }) => {
|
||||||
await logIntoElement(page, homeserver, credentials);
|
await logIntoElement(page, credentials);
|
||||||
|
|
||||||
/* Dismiss "Verify this device" */
|
/* Dismiss "Verify this device" */
|
||||||
const authPage = page.locator(".mx_AuthPage");
|
const authPage = page.locator(".mx_AuthPage");
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -53,6 +53,8 @@ test.describe("Cryptography", function () {
|
|||||||
|
|
||||||
// Even though Alice has seen Bob's join event, Bob may not have done so yet. Wait for the sync to arrive.
|
// Even though Alice has seen Bob's join event, Bob may not have done so yet. Wait for the sync to arrive.
|
||||||
await bob.awaitRoomMembership(testRoomId);
|
await bob.awaitRoomMembership(testRoomId);
|
||||||
|
|
||||||
|
await app.client.network.setupRoute();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should show the correct shield on e2e events", async ({
|
test("should show the correct shield on e2e events", async ({
|
||||||
@@ -133,8 +135,7 @@ test.describe("Cryptography", function () {
|
|||||||
"Encrypted by a device not verified by its owner.",
|
"Encrypted by a device not verified by its owner.",
|
||||||
);
|
);
|
||||||
|
|
||||||
/* In legacy crypto: should show a grey padlock for a message from a deleted device.
|
/* Should show a red padlock for a message from an unverified device.
|
||||||
* In rust crypto: should show a red padlock for a message from an unverified device.
|
|
||||||
* Rust crypto remembers the verification state of the sending device, so it will know that the device was
|
* Rust crypto remembers the verification state of the sending device, so it will know that the device was
|
||||||
* unverified, even if it gets deleted. */
|
* unverified, even if it gets deleted. */
|
||||||
// bob deletes his second device
|
// bob deletes his second device
|
||||||
@@ -168,9 +169,7 @@ test.describe("Cryptography", function () {
|
|||||||
await expect(lastE2eIcon).toHaveClass(/mx_EventTile_e2eIcon_warning/);
|
await expect(lastE2eIcon).toHaveClass(/mx_EventTile_e2eIcon_warning/);
|
||||||
await lastE2eIcon.focus();
|
await lastE2eIcon.focus();
|
||||||
await expect(await app.getTooltipForElement(lastE2eIcon)).toContainText(
|
await expect(await app.getTooltipForElement(lastE2eIcon)).toContainText(
|
||||||
workerInfo.project.name === "Legacy Crypto"
|
"Encrypted by a device not verified by its owner.",
|
||||||
? "Encrypted by an unknown or deleted device."
|
|
||||||
: "Encrypted by a device not verified by its owner.",
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -208,7 +207,7 @@ test.describe("Cryptography", function () {
|
|||||||
window.localStorage.clear();
|
window.localStorage.clear();
|
||||||
});
|
});
|
||||||
await page.reload();
|
await page.reload();
|
||||||
await logIntoElement(page, homeserver, aliceCredentials, securityKey);
|
await logIntoElement(page, aliceCredentials, securityKey);
|
||||||
|
|
||||||
/* go back to the test room and find Bob's message again */
|
/* go back to the test room and find Bob's message again */
|
||||||
await app.viewRoomById(testRoomId);
|
await app.viewRoomById(testRoomId);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2024 The Matrix.org Foundation C.I.C.
|
Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@ import { createRoom, enableKeyBackup, logIntoElement, sendMessageInCurrentRoom }
|
|||||||
|
|
||||||
test.describe("Logout tests", () => {
|
test.describe("Logout tests", () => {
|
||||||
test.beforeEach(async ({ page, homeserver, credentials }) => {
|
test.beforeEach(async ({ page, homeserver, credentials }) => {
|
||||||
await logIntoElement(page, homeserver, credentials);
|
await logIntoElement(page, credentials);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Ask to set up recovery on logout if not setup", async ({ page, app }) => {
|
test("Ask to set up recovery on logout if not setup", async ({ page, app }) => {
|
||||||
|
|||||||
@@ -2,16 +2,19 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
|
Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { readFile } from "node:fs/promises";
|
import { readFile } from "node:fs/promises";
|
||||||
|
|
||||||
import { expect, test as base } from "../../element-web-test";
|
import { expect, test } from "../../element-web-test";
|
||||||
|
|
||||||
|
test.describe("migration", { tag: "@no-webkit" }, function () {
|
||||||
|
test.use({
|
||||||
|
displayName: "Alice",
|
||||||
|
|
||||||
const test = base.extend({
|
|
||||||
// Replace the `user` fixture with one which populates the indexeddb data before starting the app.
|
// Replace the `user` fixture with one which populates the indexeddb data before starting the app.
|
||||||
user: async ({ context, pageWithCredentials: page, credentials }, use) => {
|
user: async ({ context, pageWithCredentials: page, credentials }, use) => {
|
||||||
await page.route(`/test_indexeddb_cryptostore_dump/*`, async (route, request) => {
|
await page.route(`/test_indexeddb_cryptostore_dump/*`, async (route, request) => {
|
||||||
@@ -23,13 +26,9 @@ const test = base.extend({
|
|||||||
|
|
||||||
await use(credentials);
|
await use(credentials);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
test.describe("migration", function () {
|
|
||||||
test.use({ displayName: "Alice" });
|
|
||||||
|
|
||||||
test("Should support migration from legacy crypto", async ({ context, user, page }, workerInfo) => {
|
test("Should support migration from legacy crypto", async ({ context, user, page }, workerInfo) => {
|
||||||
test.skip(workerInfo.project.name === "Legacy Crypto", "This test only works with Rust crypto.");
|
|
||||||
test.slow();
|
test.slow();
|
||||||
|
|
||||||
// We should see a migration progress bar
|
// We should see a migration progress bar
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
|
Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -138,22 +138,9 @@ export async function checkDeviceIsConnectedKeyBackup(
|
|||||||
*
|
*
|
||||||
* If a `securityKey` is given, verifies the new device using the key.
|
* If a `securityKey` is given, verifies the new device using the key.
|
||||||
*/
|
*/
|
||||||
export async function logIntoElement(
|
export async function logIntoElement(page: Page, credentials: Credentials, securityKey?: string) {
|
||||||
page: Page,
|
|
||||||
homeserver: HomeserverInstance,
|
|
||||||
credentials: Credentials,
|
|
||||||
securityKey?: string,
|
|
||||||
) {
|
|
||||||
await page.goto("/#/login");
|
await page.goto("/#/login");
|
||||||
|
|
||||||
// select homeserver
|
|
||||||
await page.getByRole("button", { name: "Edit" }).click();
|
|
||||||
await page.getByRole("textbox", { name: "Other homeserver" }).fill(homeserver.config.baseUrl);
|
|
||||||
await page.getByRole("button", { name: "Continue", exact: true }).click();
|
|
||||||
|
|
||||||
// wait for the dialog to go away
|
|
||||||
await expect(page.locator(".mx_ServerPickerDialog")).not.toBeVisible();
|
|
||||||
|
|
||||||
await page.getByRole("textbox", { name: "Username" }).fill(credentials.userId);
|
await page.getByRole("textbox", { name: "Username" }).fill(credentials.userId);
|
||||||
await page.getByPlaceholder("Password").fill(credentials.password);
|
await page.getByPlaceholder("Password").fill(credentials.password);
|
||||||
await page.getByRole("button", { name: "Sign in" }).click();
|
await page.getByRole("button", { name: "Sign in" }).click();
|
||||||
@@ -220,11 +207,7 @@ export async function doTwoWaySasVerification(page: Page, verifier: JSHandle<Ver
|
|||||||
for (let i = 0; i < emojis.length; i++) {
|
for (let i = 0; i < emojis.length; i++) {
|
||||||
const emoji = emojis[i];
|
const emoji = emojis[i];
|
||||||
const emojiBlock = emojiBlocks.nth(i);
|
const emojiBlock = emojiBlocks.nth(i);
|
||||||
const textContent = await emojiBlock.textContent();
|
await expect(emojiBlock).toHaveText(emoji[0] + emoji[1]);
|
||||||
// VerificationShowSas munges the case of the emoji descriptions returned by the js-sdk before
|
|
||||||
// displaying them. Once we drop support for legacy crypto, that code can go away, and so can the
|
|
||||||
// case-munging here.
|
|
||||||
expect(textContent.toLowerCase()).toEqual(emoji[0] + emoji[1].toLowerCase());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
48
playwright/e2e/csAPI.ts
Normal file
48
playwright/e2e/csAPI.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024 New Vector Ltd.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||||
|
Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { APIRequestContext } from "playwright-core";
|
||||||
|
import { KeyBackupInfo } from "matrix-js-sdk/src/crypto-api";
|
||||||
|
|
||||||
|
import { HomeserverInstance } from "../plugins/homeserver";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A small subset of the Client-Server API used to manipulate the state of the
|
||||||
|
* account on the homeserver independently of the client under test.
|
||||||
|
*/
|
||||||
|
export class TestClientServerAPI {
|
||||||
|
public constructor(
|
||||||
|
private request: APIRequestContext,
|
||||||
|
private homeserver: HomeserverInstance,
|
||||||
|
private accessToken: string,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public async getCurrentBackupInfo(): Promise<KeyBackupInfo | null> {
|
||||||
|
const res = await this.request.get(`${this.homeserver.baseUrl}/_matrix/client/v3/room_keys/version`, {
|
||||||
|
headers: { Authorization: `Bearer ${this.accessToken}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
return await res.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls the API directly to delete the given backup version
|
||||||
|
* @param version The version to delete
|
||||||
|
*/
|
||||||
|
public async deleteBackupVersion(version: string): Promise<void> {
|
||||||
|
const res = await this.request.delete(
|
||||||
|
`${this.homeserver.baseUrl}/_matrix/client/v3/room_keys/version/${version}`,
|
||||||
|
{
|
||||||
|
headers: { Authorization: `Bearer ${this.accessToken}` },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`Failed to delete backup version: ${res.status}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2024 The Matrix.org Foundation C.I.C.
|
Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -2,29 +2,35 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2024 The Matrix.org Foundation C.I.C.
|
Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { expect, test } from "../../element-web-test";
|
import { expect, test } from "../../element-web-test";
|
||||||
import { selectHomeserver } from "../utils";
|
import { selectHomeserver } from "../utils";
|
||||||
|
import { emailHomeserver } from "../../plugins/homeserver/synapse/emailHomeserver.ts";
|
||||||
|
import { isDendrite } from "../../plugins/homeserver/dendrite";
|
||||||
|
|
||||||
const username = "user1234";
|
const username = "user1234";
|
||||||
// this has to be password-like enough to please zxcvbn. Needless to say it's just from pwgen.
|
// this has to be password-like enough to please zxcvbn. Needless to say it's just from pwgen.
|
||||||
const password = "oETo7MPf0o";
|
const password = "oETo7MPf0o";
|
||||||
const email = "user@nowhere.dummy";
|
const email = "user@nowhere.dummy";
|
||||||
|
|
||||||
test.describe("Forgot Password", () => {
|
test.use(emailHomeserver);
|
||||||
test.use({
|
test.use({
|
||||||
startHomeserverOpts: ({ mailhog }, use) =>
|
config: {
|
||||||
use({
|
// The only thing that we really *need* (otherwise Element refuses to load) is a default homeserver.
|
||||||
template: "email",
|
// We point that to a guaranteed-invalid domain.
|
||||||
variables: {
|
default_server_config: {
|
||||||
SMTP_HOST: "host.containers.internal",
|
"m.homeserver": {
|
||||||
SMTP_PORT: mailhog.instance.smtpPort,
|
base_url: "https://server.invalid",
|
||||||
},
|
},
|
||||||
}),
|
},
|
||||||
});
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe("Forgot Password", () => {
|
||||||
|
test.skip(isDendrite, "not yet wired up");
|
||||||
|
|
||||||
test("renders properly", { tag: "@screenshot" }, async ({ page, homeserver }) => {
|
test("renders properly", { tag: "@screenshot" }, async ({ page, homeserver }) => {
|
||||||
await page.goto("/");
|
await page.goto("/");
|
||||||
@@ -32,7 +38,7 @@ test.describe("Forgot Password", () => {
|
|||||||
await page.getByRole("link", { name: "Sign in" }).click();
|
await page.getByRole("link", { name: "Sign in" }).click();
|
||||||
|
|
||||||
// need to select a homeserver at this stage, before entering the forgot password flow
|
// need to select a homeserver at this stage, before entering the forgot password flow
|
||||||
await selectHomeserver(page, homeserver.config.baseUrl);
|
await selectHomeserver(page, homeserver.baseUrl);
|
||||||
|
|
||||||
await page.getByRole("button", { name: "Forgot password?" }).click();
|
await page.getByRole("button", { name: "Forgot password?" }).click();
|
||||||
|
|
||||||
@@ -47,7 +53,7 @@ test.describe("Forgot Password", () => {
|
|||||||
await page.goto("/");
|
await page.goto("/");
|
||||||
|
|
||||||
await page.getByRole("link", { name: "Sign in" }).click();
|
await page.getByRole("link", { name: "Sign in" }).click();
|
||||||
await selectHomeserver(page, homeserver.config.baseUrl);
|
await selectHomeserver(page, homeserver.baseUrl);
|
||||||
|
|
||||||
await page.getByRole("button", { name: "Forgot password?" }).click();
|
await page.getByRole("button", { name: "Forgot password?" }).click();
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,14 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Page } from "@playwright/test";
|
import type { Page } from "@playwright/test";
|
||||||
import { test, expect } from "../../element-web-test";
|
import { test, expect } from "../../element-web-test";
|
||||||
import { openIntegrationManager } from "./utils";
|
import { openIntegrationManager } from "./utils";
|
||||||
|
import type { UserWidget } from "../../../src/utils/WidgetUtils-types.ts";
|
||||||
|
|
||||||
const ROOM_NAME = "Integration Manager Test";
|
const ROOM_NAME = "Integration Manager Test";
|
||||||
|
|
||||||
@@ -92,7 +93,7 @@ test.describe("Integration Manager: Get OpenID Token", () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
id: "integration-manager",
|
id: "integration-manager",
|
||||||
},
|
} as unknown as UserWidget,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Succeed when checking the token is valid
|
// Succeed when checking the token is valid
|
||||||
|
|||||||
@@ -2,13 +2,14 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Page } from "@playwright/test";
|
import type { Page } from "@playwright/test";
|
||||||
import { test, expect } from "../../element-web-test";
|
import { test, expect } from "../../element-web-test";
|
||||||
import { openIntegrationManager } from "./utils";
|
import { openIntegrationManager } from "./utils";
|
||||||
|
import type { UserWidget } from "../../../src/utils/WidgetUtils-types.ts";
|
||||||
|
|
||||||
const ROOM_NAME = "Integration Manager Test";
|
const ROOM_NAME = "Integration Manager Test";
|
||||||
const USER_DISPLAY_NAME = "Alice";
|
const USER_DISPLAY_NAME = "Alice";
|
||||||
@@ -136,7 +137,7 @@ test.describe("Integration Manager: Kick", () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
id: "integration-manager",
|
id: "integration-manager",
|
||||||
},
|
} as unknown as UserWidget,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Succeed when checking the token is valid
|
// Succeed when checking the token is valid
|
||||||
|
|||||||
@@ -2,13 +2,14 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Page } from "@playwright/test";
|
import type { Page } from "@playwright/test";
|
||||||
import { test, expect } from "../../element-web-test";
|
import { test, expect } from "../../element-web-test";
|
||||||
import { openIntegrationManager } from "./utils";
|
import { openIntegrationManager } from "./utils";
|
||||||
|
import type { UserWidget } from "../../../src/utils/WidgetUtils-types.ts";
|
||||||
|
|
||||||
const ROOM_NAME = "Integration Manager Test";
|
const ROOM_NAME = "Integration Manager Test";
|
||||||
|
|
||||||
@@ -107,7 +108,7 @@ test.describe("Integration Manager: Read Events", () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
id: "integration-manager",
|
id: "integration-manager",
|
||||||
},
|
} as unknown as UserWidget,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Succeed when checking the token is valid
|
// Succeed when checking the token is valid
|
||||||
|
|||||||
@@ -2,13 +2,14 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Page } from "@playwright/test";
|
import type { Page } from "@playwright/test";
|
||||||
import { test, expect } from "../../element-web-test";
|
import { test, expect } from "../../element-web-test";
|
||||||
import { openIntegrationManager } from "./utils";
|
import { openIntegrationManager } from "./utils";
|
||||||
|
import type { UserWidget } from "../../../src/utils/WidgetUtils-types.ts";
|
||||||
|
|
||||||
const ROOM_NAME = "Integration Manager Test";
|
const ROOM_NAME = "Integration Manager Test";
|
||||||
|
|
||||||
@@ -113,7 +114,7 @@ test.describe("Integration Manager: Send Event", () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
id: "integration-manager",
|
id: "integration-manager",
|
||||||
},
|
} as unknown as UserWidget,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Succeed when checking the token is valid
|
// Succeed when checking the token is valid
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ Copyright 2024 New Vector Ltd.
|
|||||||
Copyright 2023 Suguru Hirahara
|
Copyright 2023 Suguru Hirahara
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Copyright 2023 Mikhail Aheichyk
|
|||||||
Copyright 2023 Nordeck IT + Consulting GmbH.
|
Copyright 2023 Nordeck IT + Consulting GmbH.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Copyright 2023 Mikhail Aheichyk
|
|||||||
Copyright 2023 Nordeck IT + Consulting GmbH.
|
Copyright 2023 Nordeck IT + Consulting GmbH.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -25,12 +25,13 @@ test.describe("Lazy Loading", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test.beforeEach(async ({ page, homeserver, user, bot }) => {
|
test.beforeEach(async ({ page, homeserver, user, bot, app }) => {
|
||||||
for (let i = 1; i <= 10; i++) {
|
for (let i = 1; i <= 10; i++) {
|
||||||
const displayName = `Charly #${i}`;
|
const displayName = `Charly #${i}`;
|
||||||
const bot = new Bot(page, homeserver, { displayName, startClient: false, autoAcceptInvites: false });
|
const bot = new Bot(page, homeserver, { displayName, startClient: false, autoAcceptInvites: false });
|
||||||
charlies.push(bot);
|
charlies.push(bot);
|
||||||
}
|
}
|
||||||
|
await app.client.network.setupRoute();
|
||||||
});
|
});
|
||||||
|
|
||||||
const name = "Lazy Loading Test";
|
const name = "Lazy Loading Test";
|
||||||
@@ -77,7 +78,7 @@ test.describe("Lazy Loading", () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getMemberInMemberlist(page: Page, name: string): Locator {
|
function getMemberInMemberlist(page: Page, name: string): Locator {
|
||||||
return page.locator(".mx_MemberList .mx_EntityTile_name").filter({ hasText: name });
|
return page.locator(".mx_MemberListView .mx_MemberTileView_name").filter({ hasText: name });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkMemberList(page: Page, charlies: Bot[]) {
|
async function checkMemberList(page: Page, charlies: Bot[]) {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 Suguru Hirahara
|
Copyright 2023 Suguru Hirahara
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -10,7 +10,8 @@ import { Locator, Page } from "@playwright/test";
|
|||||||
|
|
||||||
import { test, expect } from "../../element-web-test";
|
import { test, expect } from "../../element-web-test";
|
||||||
|
|
||||||
test.describe("Location sharing", () => {
|
// Firefox headless lacks WebGL support https://bugzilla.mozilla.org/show_bug.cgi?id=1375585
|
||||||
|
test.describe("Location sharing", { tag: "@no-firefox" }, () => {
|
||||||
const selectLocationShareTypeOption = (page: Page, shareType: string): Locator => {
|
const selectLocationShareTypeOption = (page: Page, shareType: string): Locator => {
|
||||||
return page.getByTestId(`share-location-option-${shareType}`);
|
return page.getByTestId(`share-location-option-${shareType}`);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,18 +2,19 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { test, expect } from "../../element-web-test";
|
import { test, expect } from "../../element-web-test";
|
||||||
|
import { consentHomeserver } from "../../plugins/homeserver/synapse/consentHomeserver.ts";
|
||||||
|
|
||||||
|
test.use(consentHomeserver);
|
||||||
|
test.use({
|
||||||
|
displayName: "Bob",
|
||||||
|
});
|
||||||
|
|
||||||
test.describe("Consent", () => {
|
test.describe("Consent", () => {
|
||||||
test.use({
|
|
||||||
startHomeserverOpts: "consent",
|
|
||||||
displayName: "Bob",
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should prompt the user to consent to terms when server deems it necessary", async ({
|
test("should prompt the user to consent to terms when server deems it necessary", async ({
|
||||||
context,
|
context,
|
||||||
page,
|
page,
|
||||||
|
|||||||
@@ -2,18 +2,18 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Page } from "playwright-core";
|
import { Page } from "playwright-core";
|
||||||
|
|
||||||
import { expect, test } from "../../element-web-test";
|
import { expect, test } from "../../element-web-test";
|
||||||
import { doTokenRegistration } from "./utils";
|
|
||||||
import { isDendrite } from "../../plugins/homeserver/dendrite";
|
|
||||||
import { selectHomeserver } from "../utils";
|
import { selectHomeserver } from "../utils";
|
||||||
import { Credentials, HomeserverInstance } from "../../plugins/homeserver";
|
import { Credentials, HomeserverInstance } from "../../plugins/homeserver";
|
||||||
|
import { consentHomeserver } from "../../plugins/homeserver/synapse/consentHomeserver.ts";
|
||||||
|
|
||||||
|
// This test requires fixed credentials for the device signing keys below to work
|
||||||
const username = "user1234";
|
const username = "user1234";
|
||||||
const password = "p4s5W0rD";
|
const password = "p4s5W0rD";
|
||||||
|
|
||||||
@@ -68,26 +68,42 @@ const DEVICE_SIGNING_KEYS_BODY = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
async function login(page: Page, homeserver: HomeserverInstance) {
|
async function login(page: Page, homeserver: HomeserverInstance, credentials: Credentials) {
|
||||||
await page.getByRole("link", { name: "Sign in" }).click();
|
await page.getByRole("link", { name: "Sign in" }).click();
|
||||||
await selectHomeserver(page, homeserver.config.baseUrl);
|
await selectHomeserver(page, homeserver.baseUrl);
|
||||||
|
|
||||||
await page.getByRole("textbox", { name: "Username" }).fill(username);
|
await page.getByRole("textbox", { name: "Username" }).fill(credentials.username);
|
||||||
await page.getByPlaceholder("Password").fill(password);
|
await page.getByPlaceholder("Password").fill(credentials.password);
|
||||||
await page.getByRole("button", { name: "Sign in" }).click();
|
await page.getByRole("button", { name: "Sign in" }).click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test.use(consentHomeserver);
|
||||||
|
test.use({
|
||||||
|
config: {
|
||||||
|
// The only thing that we really *need* (otherwise Element refuses to load) is a default homeserver.
|
||||||
|
// We point that to a guaranteed-invalid domain.
|
||||||
|
default_server_config: {
|
||||||
|
"m.homeserver": {
|
||||||
|
base_url: "https://server.invalid",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
credentials: async ({ context, homeserver }, use) => {
|
||||||
|
const displayName = "Dave";
|
||||||
|
const credentials = await homeserver.registerUser(username, password, displayName);
|
||||||
|
console.log(`Registered test user @user:localhost with displayname ${displayName}`);
|
||||||
|
|
||||||
|
await use({
|
||||||
|
...credentials,
|
||||||
|
displayName,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
test.describe("Login", () => {
|
test.describe("Login", () => {
|
||||||
test.describe("Password login", () => {
|
test.describe("Password login", () => {
|
||||||
test.use({ startHomeserverOpts: "consent" });
|
|
||||||
|
|
||||||
let creds: Credentials;
|
|
||||||
|
|
||||||
test.beforeEach(async ({ homeserver }) => {
|
|
||||||
creds = await homeserver.registerUser(username, password);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("Loads the welcome page by default; then logs in with an existing account and lands on the home screen", async ({
|
test("Loads the welcome page by default; then logs in with an existing account and lands on the home screen", async ({
|
||||||
|
credentials,
|
||||||
page,
|
page,
|
||||||
homeserver,
|
homeserver,
|
||||||
checkA11y,
|
checkA11y,
|
||||||
@@ -101,7 +117,7 @@ test.describe("Login", () => {
|
|||||||
await page.getByRole("link", { name: "Sign in" }).click();
|
await page.getByRole("link", { name: "Sign in" }).click();
|
||||||
|
|
||||||
// first pick the homeserver, as otherwise the user picker won't be visible
|
// first pick the homeserver, as otherwise the user picker won't be visible
|
||||||
await selectHomeserver(page, homeserver.config.baseUrl);
|
await selectHomeserver(page, homeserver.baseUrl);
|
||||||
|
|
||||||
await page.getByRole("button", { name: "Edit" }).click();
|
await page.getByRole("button", { name: "Edit" }).click();
|
||||||
|
|
||||||
@@ -114,23 +130,23 @@ test.describe("Login", () => {
|
|||||||
await expect(page.locator(".mx_ServerPicker_server")).toHaveText("server.invalid");
|
await expect(page.locator(".mx_ServerPicker_server")).toHaveText("server.invalid");
|
||||||
|
|
||||||
// switch back to the custom homeserver
|
// switch back to the custom homeserver
|
||||||
await selectHomeserver(page, homeserver.config.baseUrl);
|
await selectHomeserver(page, homeserver.baseUrl);
|
||||||
|
|
||||||
await expect(page.getByRole("textbox", { name: "Username" })).toBeVisible();
|
await expect(page.getByRole("textbox", { name: "Username" })).toBeVisible();
|
||||||
// Disabled because flaky - see https://github.com/vector-im/element-web/issues/24688
|
// Disabled because flaky - see https://github.com/vector-im/element-web/issues/24688
|
||||||
// cy.percySnapshot("Login");
|
// cy.percySnapshot("Login");
|
||||||
await checkA11y();
|
await checkA11y();
|
||||||
|
|
||||||
await page.getByRole("textbox", { name: "Username" }).fill(username);
|
await page.getByRole("textbox", { name: "Username" }).fill(credentials.username);
|
||||||
await page.getByPlaceholder("Password").fill(password);
|
await page.getByPlaceholder("Password").fill(credentials.password);
|
||||||
await page.getByRole("button", { name: "Sign in" }).click();
|
await page.getByRole("button", { name: "Sign in" }).click();
|
||||||
|
|
||||||
await expect(page).toHaveURL(/\/#\/home$/);
|
await expect(page).toHaveURL(/\/#\/home$/);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Follows the original link after login", async ({ page, homeserver }) => {
|
test("Follows the original link after login", async ({ page, homeserver, credentials }) => {
|
||||||
await page.goto("/#/room/!room:id"); // should redirect to the welcome page
|
await page.goto("/#/room/!room:id"); // should redirect to the welcome page
|
||||||
await login(page, homeserver);
|
await login(page, homeserver, credentials);
|
||||||
|
|
||||||
await expect(page).toHaveURL(/\/#\/room\/!room:id$/);
|
await expect(page).toHaveURL(/\/#\/room\/!room:id$/);
|
||||||
await expect(page.getByRole("button", { name: "Join the discussion" })).toBeVisible();
|
await expect(page.getByRole("button", { name: "Join the discussion" })).toBeVisible();
|
||||||
@@ -141,18 +157,19 @@ test.describe("Login", () => {
|
|||||||
page,
|
page,
|
||||||
homeserver,
|
homeserver,
|
||||||
request,
|
request,
|
||||||
|
credentials,
|
||||||
}) => {
|
}) => {
|
||||||
const res = await request.post(
|
const res = await request.post(`${homeserver.baseUrl}/_matrix/client/v3/keys/device_signing/upload`, {
|
||||||
`${homeserver.config.baseUrl}/_matrix/client/v3/keys/device_signing/upload`,
|
headers: { Authorization: `Bearer ${credentials.accessToken}` },
|
||||||
{ headers: { Authorization: `Bearer ${creds.accessToken}` }, data: DEVICE_SIGNING_KEYS_BODY },
|
data: DEVICE_SIGNING_KEYS_BODY,
|
||||||
);
|
});
|
||||||
if (res.status() / 100 !== 2) {
|
if (res.status() / 100 !== 2) {
|
||||||
console.log("Uploading dummy keys failed", await res.json());
|
console.log("Uploading dummy keys failed", await res.json());
|
||||||
}
|
}
|
||||||
expect(res.status() / 100).toEqual(2);
|
expect(res.status() / 100).toEqual(2);
|
||||||
|
|
||||||
await page.goto("/");
|
await page.goto("/");
|
||||||
await login(page, homeserver);
|
await login(page, homeserver, credentials);
|
||||||
|
|
||||||
await expect(page.getByRole("heading", { name: "Verify this device", level: 1 })).toBeVisible();
|
await expect(page.getByRole("heading", { name: "Verify this device", level: 1 })).toBeVisible();
|
||||||
|
|
||||||
@@ -170,10 +187,14 @@ test.describe("Login", () => {
|
|||||||
page,
|
page,
|
||||||
homeserver,
|
homeserver,
|
||||||
request,
|
request,
|
||||||
|
credentials,
|
||||||
}) => {
|
}) => {
|
||||||
const res = await request.post(
|
const res = await request.post(
|
||||||
`${homeserver.config.baseUrl}/_matrix/client/v3/keys/device_signing/upload`,
|
`${homeserver.baseUrl}/_matrix/client/v3/keys/device_signing/upload`,
|
||||||
{ headers: { Authorization: `Bearer ${creds.accessToken}` }, data: DEVICE_SIGNING_KEYS_BODY },
|
{
|
||||||
|
headers: { Authorization: `Bearer ${credentials.accessToken}` },
|
||||||
|
data: DEVICE_SIGNING_KEYS_BODY,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
if (res.status() / 100 !== 2) {
|
if (res.status() / 100 !== 2) {
|
||||||
console.log("Uploading dummy keys failed", await res.json());
|
console.log("Uploading dummy keys failed", await res.json());
|
||||||
@@ -181,7 +202,7 @@ test.describe("Login", () => {
|
|||||||
expect(res.status() / 100).toEqual(2);
|
expect(res.status() / 100).toEqual(2);
|
||||||
|
|
||||||
await page.goto("/");
|
await page.goto("/");
|
||||||
await login(page, homeserver);
|
await login(page, homeserver, credentials);
|
||||||
|
|
||||||
await expect(page.getByRole("heading", { name: "Verify this device", level: 1 })).toBeVisible();
|
await expect(page.getByRole("heading", { name: "Verify this device", level: 1 })).toBeVisible();
|
||||||
|
|
||||||
@@ -200,11 +221,15 @@ test.describe("Login", () => {
|
|||||||
page,
|
page,
|
||||||
homeserver,
|
homeserver,
|
||||||
request,
|
request,
|
||||||
|
credentials,
|
||||||
}) => {
|
}) => {
|
||||||
console.log(`uid ${creds.userId} body`, DEVICE_SIGNING_KEYS_BODY);
|
console.log(`uid ${credentials.userId} body`, DEVICE_SIGNING_KEYS_BODY);
|
||||||
const res = await request.post(
|
const res = await request.post(
|
||||||
`${homeserver.config.baseUrl}/_matrix/client/v3/keys/device_signing/upload`,
|
`${homeserver.baseUrl}/_matrix/client/v3/keys/device_signing/upload`,
|
||||||
{ headers: { Authorization: `Bearer ${creds.accessToken}` }, data: DEVICE_SIGNING_KEYS_BODY },
|
{
|
||||||
|
headers: { Authorization: `Bearer ${credentials.accessToken}` },
|
||||||
|
data: DEVICE_SIGNING_KEYS_BODY,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
if (res.status() / 100 !== 2) {
|
if (res.status() / 100 !== 2) {
|
||||||
console.log("Uploading dummy keys failed", await res.json());
|
console.log("Uploading dummy keys failed", await res.json());
|
||||||
@@ -212,9 +237,9 @@ test.describe("Login", () => {
|
|||||||
expect(res.status() / 100).toEqual(2);
|
expect(res.status() / 100).toEqual(2);
|
||||||
|
|
||||||
await page.goto("/");
|
await page.goto("/");
|
||||||
await login(page, homeserver);
|
await login(page, homeserver, credentials);
|
||||||
|
|
||||||
const h1 = await page.getByRole("heading", { name: "Verify this device", level: 1 });
|
const h1 = page.getByRole("heading", { name: "Verify this device", level: 1 });
|
||||||
await expect(h1).toBeVisible();
|
await expect(h1).toBeVisible();
|
||||||
|
|
||||||
await expect(h1.locator(".mx_CompleteSecurity_skip")).toHaveCount(0);
|
await expect(h1.locator(".mx_CompleteSecurity_skip")).toHaveCount(0);
|
||||||
@@ -223,32 +248,7 @@ test.describe("Login", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// tests for old-style SSO login, in which we exchange tokens with Synapse, and Synapse talks to an auth server
|
|
||||||
test.describe("SSO login", () => {
|
|
||||||
test.skip(isDendrite, "does not yet support SSO");
|
|
||||||
|
|
||||||
test.use({
|
|
||||||
startHomeserverOpts: ({ oAuthServer }, use) =>
|
|
||||||
use({
|
|
||||||
template: "default",
|
|
||||||
oAuthServerPort: oAuthServer.port,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
test("logs in with SSO and lands on the home screen", async ({ page, homeserver }) => {
|
|
||||||
// If this test fails with a screen showing "Timeout connecting to remote server", it is most likely due to
|
|
||||||
// your firewall settings: Synapse is unable to reach the OIDC server.
|
|
||||||
//
|
|
||||||
// If you are using ufw, try something like:
|
|
||||||
// sudo ufw allow in on docker0
|
|
||||||
//
|
|
||||||
await doTokenRegistration(page, homeserver);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test.describe("logout", () => {
|
test.describe("logout", () => {
|
||||||
test.use({ startHomeserverOpts: "consent" });
|
|
||||||
|
|
||||||
test("should go to login page on logout", async ({ page, user }) => {
|
test("should go to login page on logout", async ({ page, user }) => {
|
||||||
await page.getByRole("button", { name: "User menu" }).click();
|
await page.getByRole("button", { name: "User menu" }).click();
|
||||||
await expect(page.getByText(user.displayName, { exact: true })).toBeVisible();
|
await expect(page.getByText(user.displayName, { exact: true })).toBeVisible();
|
||||||
@@ -260,29 +260,4 @@ test.describe("Login", () => {
|
|||||||
await expect(page).toHaveURL(/\/#\/login$/);
|
await expect(page).toHaveURL(/\/#\/login$/);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test.describe("logout with logout_redirect_url", () => {
|
|
||||||
test.use({
|
|
||||||
startHomeserverOpts: "consent",
|
|
||||||
config: {
|
|
||||||
// We redirect to decoder-ring because it's a predictable page that isn't Element itself.
|
|
||||||
// We could use example.org, matrix.org, or something else, however this puts dependency of external
|
|
||||||
// infrastructure on our tests. In the same vein, we don't really want to figure out how to ship a
|
|
||||||
// `test-landing.html` page when running with an uncontrolled Element (via `yarn start`).
|
|
||||||
// Using the decoder-ring is just as fine, and we can search for strategic names.
|
|
||||||
logout_redirect_url: "/decoder-ring/",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should respect logout_redirect_url", async ({ page, user }) => {
|
|
||||||
await page.getByRole("button", { name: "User menu" }).click();
|
|
||||||
await expect(page.getByText(user.displayName, { exact: true })).toBeVisible();
|
|
||||||
|
|
||||||
// give a change for the outstanding requests queue to settle before logging out
|
|
||||||
await page.waitForTimeout(2000);
|
|
||||||
|
|
||||||
await page.locator(".mx_UserMenu_contextMenu").getByRole("menuitem", { name: "Sign out" }).click();
|
|
||||||
await expect(page).toHaveURL(/\/decoder-ring\/$/);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
29
playwright/e2e/login/login-sso.spec.ts
Normal file
29
playwright/e2e/login/login-sso.spec.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024 New Vector Ltd.
|
||||||
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||||
|
Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { test } from "../../element-web-test";
|
||||||
|
import { doTokenRegistration } from "./utils";
|
||||||
|
import { isDendrite } from "../../plugins/homeserver/dendrite";
|
||||||
|
import { legacyOAuthHomeserver } from "../../plugins/homeserver/synapse/legacyOAuthHomeserver.ts";
|
||||||
|
|
||||||
|
test.use(legacyOAuthHomeserver);
|
||||||
|
|
||||||
|
// tests for old-style SSO login, in which we exchange tokens with Synapse, and Synapse talks to an auth server
|
||||||
|
test.describe("SSO login", () => {
|
||||||
|
test.skip(isDendrite, "does not yet support SSO");
|
||||||
|
|
||||||
|
test("logs in with SSO and lands on the home screen", async ({ page, homeserver }) => {
|
||||||
|
// If this test fails with a screen showing "Timeout connecting to remote server", it is most likely due to
|
||||||
|
// your firewall settings: Synapse is unable to reach the OIDC server.
|
||||||
|
//
|
||||||
|
// If you are using ufw, try something like:
|
||||||
|
// sudo ufw allow in on docker0
|
||||||
|
//
|
||||||
|
await doTokenRegistration(page, homeserver);
|
||||||
|
});
|
||||||
|
});
|
||||||
35
playwright/e2e/login/logout_redirect_url.spec.ts
Normal file
35
playwright/e2e/login/logout_redirect_url.spec.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024 New Vector Ltd.
|
||||||
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||||
|
Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { expect, test } from "../../element-web-test";
|
||||||
|
import { consentHomeserver } from "../../plugins/homeserver/synapse/consentHomeserver.ts";
|
||||||
|
|
||||||
|
test.use(consentHomeserver);
|
||||||
|
test.use({
|
||||||
|
config: {
|
||||||
|
// We redirect to decoder-ring because it's a predictable page that isn't Element itself.
|
||||||
|
// We could use example.org, matrix.org, or something else, however this puts dependency of external
|
||||||
|
// infrastructure on our tests. In the same vein, we don't really want to figure out how to ship a
|
||||||
|
// `test-landing.html` page when running with an uncontrolled Element (via `yarn start`).
|
||||||
|
// Using the decoder-ring is just as fine, and we can search for strategic names.
|
||||||
|
logout_redirect_url: "/decoder-ring/",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe("logout with logout_redirect_url", () => {
|
||||||
|
test("should respect logout_redirect_url", async ({ page, user }) => {
|
||||||
|
await page.getByRole("button", { name: "User menu" }).click();
|
||||||
|
await expect(page.getByText(user.displayName, { exact: true })).toBeVisible();
|
||||||
|
|
||||||
|
// give a change for the outstanding requests queue to settle before logging out
|
||||||
|
await page.waitForTimeout(2000);
|
||||||
|
|
||||||
|
await page.locator(".mx_UserMenu_contextMenu").getByRole("menuitem", { name: "Sign out" }).click();
|
||||||
|
await expect(page).toHaveURL(/\/decoder-ring\/$/);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2024 The Matrix.org Foundation C.I.C.
|
Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ test.describe("Overwrite login action", () => {
|
|||||||
// This seems terminally flakey: https://github.com/element-hq/element-web/issues/27363
|
// This seems terminally flakey: https://github.com/element-hq/element-web/issues/27363
|
||||||
// I tried verious things to try & deflake it, to no avail: https://github.com/matrix-org/matrix-react-sdk/pull/12506
|
// I tried verious things to try & deflake it, to no avail: https://github.com/matrix-org/matrix-react-sdk/pull/12506
|
||||||
test.skip("Try replace existing login with new one", async ({ page, app, credentials, homeserver }) => {
|
test.skip("Try replace existing login with new one", async ({ page, app, credentials, homeserver }) => {
|
||||||
await logIntoElement(page, homeserver, credentials);
|
await logIntoElement(page, credentials);
|
||||||
|
|
||||||
const userMenu = await app.openUserMenu();
|
const userMenu = await app.openUserMenu();
|
||||||
await expect(userMenu.getByText(credentials.userId)).toBeVisible();
|
await expect(userMenu.getByText(credentials.userId)).toBeVisible();
|
||||||
@@ -24,7 +24,7 @@ test.describe("Overwrite login action", () => {
|
|||||||
expect(credentials.userId).not.toBe(bobRegister.userId);
|
expect(credentials.userId).not.toBe(bobRegister.userId);
|
||||||
|
|
||||||
const clientCredentials /* IMatrixClientCreds */ = {
|
const clientCredentials /* IMatrixClientCreds */ = {
|
||||||
homeserverUrl: homeserver.config.baseUrl,
|
homeserverUrl: homeserver.baseUrl,
|
||||||
...bobRegister,
|
...bobRegister,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,28 +2,27 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Page } from "@playwright/test";
|
|
||||||
|
|
||||||
import { test, expect } from "../../element-web-test";
|
import { test, expect } from "../../element-web-test";
|
||||||
import { doTokenRegistration } from "./utils";
|
import { interceptRequestsWithSoftLogout } from "./utils";
|
||||||
import { Credentials } from "../../plugins/homeserver";
|
|
||||||
import { isDendrite } from "../../plugins/homeserver/dendrite";
|
|
||||||
|
|
||||||
test.describe("Soft logout", () => {
|
test.use({
|
||||||
test.use({
|
|
||||||
displayName: "Alice",
|
displayName: "Alice",
|
||||||
startHomeserverOpts: ({ oAuthServer }, use) =>
|
config: {
|
||||||
use({
|
// The only thing that we really *need* (otherwise Element refuses to load) is a default homeserver.
|
||||||
template: "default",
|
// We point that to a guaranteed-invalid domain.
|
||||||
oAuthServerPort: oAuthServer.port,
|
default_server_config: {
|
||||||
}),
|
"m.homeserver": {
|
||||||
});
|
base_url: "https://server.invalid",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
test.describe("with password user", () => {
|
test.describe("Soft logout with password user", () => {
|
||||||
test("shows the soft-logout page when a request fails, and allows a re-login", async ({ page, user }) => {
|
test("shows the soft-logout page when a request fails, and allows a re-login", async ({ page, user }) => {
|
||||||
await interceptRequestsWithSoftLogout(page, user);
|
await interceptRequestsWithSoftLogout(page, user);
|
||||||
await expect(page.getByText("You're signed out")).toBeVisible();
|
await expect(page.getByText("You're signed out")).toBeVisible();
|
||||||
@@ -32,91 +31,13 @@ test.describe("Soft logout", () => {
|
|||||||
|
|
||||||
// back to the welcome page
|
// back to the welcome page
|
||||||
await expect(page).toHaveURL(/\/#\/home/);
|
await expect(page).toHaveURL(/\/#\/home/);
|
||||||
await expect(page.getByRole("heading", { name: `Welcome ${user.userId}`, exact: true })).toBeVisible();
|
await expect(page.getByRole("heading", { name: "Now, let's help you get started", exact: true })).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("still shows the soft-logout page when the page is reloaded after a soft-logout", async ({
|
test("still shows the soft-logout page when the page is reloaded after a soft-logout", async ({ page, user }) => {
|
||||||
page,
|
|
||||||
user,
|
|
||||||
}) => {
|
|
||||||
await interceptRequestsWithSoftLogout(page, user);
|
await interceptRequestsWithSoftLogout(page, user);
|
||||||
await expect(page.getByText("You're signed out")).toBeVisible();
|
await expect(page.getByText("You're signed out")).toBeVisible();
|
||||||
await page.reload();
|
await page.reload();
|
||||||
await expect(page.getByText("You're signed out")).toBeVisible();
|
await expect(page.getByText("You're signed out")).toBeVisible();
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
test.describe("with SSO user", () => {
|
|
||||||
test.skip(isDendrite, "does not yet support SSO");
|
|
||||||
|
|
||||||
test.use({
|
|
||||||
user: async ({ page, homeserver }, use) => {
|
|
||||||
const user = await doTokenRegistration(page, homeserver);
|
|
||||||
|
|
||||||
// Eventually, we should end up at the home screen.
|
|
||||||
await expect(page).toHaveURL(/\/#\/home$/);
|
|
||||||
await expect(page.getByRole("heading", { name: "Welcome Alice", exact: true })).toBeVisible();
|
|
||||||
|
|
||||||
await use(user);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
test("shows the soft-logout page when a request fails, and allows a re-login", async ({ page, user }) => {
|
|
||||||
await expect(page.getByRole("heading", { name: "Welcome Alice", exact: true })).toBeVisible();
|
|
||||||
|
|
||||||
await interceptRequestsWithSoftLogout(page, user);
|
|
||||||
|
|
||||||
await expect(page.getByText("You're signed out")).toBeVisible();
|
|
||||||
await page.getByRole("button", { name: "Continue with OAuth test" }).click();
|
|
||||||
|
|
||||||
// click the submit button
|
|
||||||
await page.getByRole("button", { name: "Submit" }).click();
|
|
||||||
|
|
||||||
// Synapse prompts us to grant permission to Element
|
|
||||||
await expect(page.getByRole("heading", { name: "Continue to your account" })).toBeVisible();
|
|
||||||
await page.getByRole("link", { name: "Continue" }).click();
|
|
||||||
|
|
||||||
// back to the welcome page
|
|
||||||
await expect(page).toHaveURL(/\/#\/home$/);
|
|
||||||
await expect(page.getByRole("heading", { name: "Welcome Alice", exact: true })).toBeVisible();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Intercept calls to /sync and have them fail with a soft-logout
|
|
||||||
*
|
|
||||||
* Any further requests to /sync with the same access token are blocked.
|
|
||||||
*/
|
|
||||||
async function interceptRequestsWithSoftLogout(page: Page, user: Credentials): Promise<void> {
|
|
||||||
await page.route("**/_matrix/client/*/sync*", async (route, req) => {
|
|
||||||
const accessToken = await req.headerValue("Authorization");
|
|
||||||
|
|
||||||
// now, if the access token on this request matches the expired one, block it
|
|
||||||
if (accessToken === `Bearer ${user.accessToken}`) {
|
|
||||||
console.log("Intercepting request with soft-logged-out access token");
|
|
||||||
await route.fulfill({
|
|
||||||
status: 401,
|
|
||||||
json: {
|
|
||||||
errcode: "M_UNKNOWN_TOKEN",
|
|
||||||
error: "Soft logout",
|
|
||||||
soft_logout: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise, pass through as normal
|
|
||||||
await route.continue();
|
|
||||||
});
|
|
||||||
|
|
||||||
const promise = page.waitForResponse((resp) => resp.url().includes("/sync") && resp.status() === 401);
|
|
||||||
|
|
||||||
// do something to make the active /sync return: create a new room
|
|
||||||
await page.evaluate(() => {
|
|
||||||
// don't wait for this to complete: it probably won't, because of the broken sync
|
|
||||||
window.mxMatrixClientPeg.get().createRoom({});
|
|
||||||
});
|
|
||||||
|
|
||||||
await promise;
|
|
||||||
}
|
|
||||||
|
|||||||
59
playwright/e2e/login/soft_logout_oauth.spec.ts
Normal file
59
playwright/e2e/login/soft_logout_oauth.spec.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024 New Vector Ltd.
|
||||||
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { test, expect } from "../../element-web-test";
|
||||||
|
import { doTokenRegistration, interceptRequestsWithSoftLogout } from "./utils";
|
||||||
|
import { legacyOAuthHomeserver } from "../../plugins/homeserver/synapse/legacyOAuthHomeserver.ts";
|
||||||
|
|
||||||
|
test.use({
|
||||||
|
displayName: "Alice",
|
||||||
|
config: {
|
||||||
|
// The only thing that we really *need* (otherwise Element refuses to load) is a default homeserver.
|
||||||
|
// We point that to a guaranteed-invalid domain.
|
||||||
|
default_server_config: {
|
||||||
|
"m.homeserver": {
|
||||||
|
base_url: "https://server.invalid",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
test.use(legacyOAuthHomeserver);
|
||||||
|
test.describe("Soft logout with SSO user", () => {
|
||||||
|
test.use({
|
||||||
|
user: async ({ page, homeserver }, use) => {
|
||||||
|
const user = await doTokenRegistration(page, homeserver);
|
||||||
|
|
||||||
|
// Eventually, we should end up at the home screen.
|
||||||
|
await expect(page).toHaveURL(/\/#\/home$/);
|
||||||
|
await expect(page.getByRole("heading", { name: "Welcome Alice", exact: true })).toBeVisible();
|
||||||
|
|
||||||
|
await use(user);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
test("shows the soft-logout page when a request fails, and allows a re-login", async ({ page, user }) => {
|
||||||
|
await expect(page.getByRole("heading", { name: "Welcome Alice", exact: true })).toBeVisible();
|
||||||
|
|
||||||
|
await interceptRequestsWithSoftLogout(page, user);
|
||||||
|
|
||||||
|
await expect(page.getByText("You're signed out")).toBeVisible();
|
||||||
|
await page.getByRole("button", { name: "Continue with OAuth test" }).click();
|
||||||
|
|
||||||
|
// click the submit button
|
||||||
|
await page.getByRole("button", { name: "Submit" }).click();
|
||||||
|
|
||||||
|
// Synapse prompts us to grant permission to Element
|
||||||
|
await expect(page.getByRole("heading", { name: "Continue to your account" })).toBeVisible();
|
||||||
|
await page.getByRole("link", { name: "Continue" }).click();
|
||||||
|
|
||||||
|
// back to the welcome page
|
||||||
|
await expect(page).toHaveURL(/\/#\/home$/);
|
||||||
|
await expect(page.getByRole("heading", { name: "Welcome Alice", exact: true })).toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -19,8 +19,8 @@ export async function doTokenRegistration(
|
|||||||
await page.goto("/#/login");
|
await page.goto("/#/login");
|
||||||
|
|
||||||
await page.getByRole("button", { name: "Edit" }).click();
|
await page.getByRole("button", { name: "Edit" }).click();
|
||||||
await page.getByRole("textbox", { name: "Other homeserver" }).fill(homeserver.config.baseUrl);
|
await page.getByRole("textbox", { name: "Other homeserver" }).fill(homeserver.baseUrl);
|
||||||
await page.getByRole("button", { name: "Continue" }).click();
|
await page.getByRole("button", { name: "Continue", exact: true }).click();
|
||||||
// wait for the dialog to go away
|
// wait for the dialog to go away
|
||||||
await expect(page.locator(".mx_ServerPickerDialog")).toHaveCount(0);
|
await expect(page.locator(".mx_ServerPickerDialog")).toHaveCount(0);
|
||||||
|
|
||||||
@@ -56,5 +56,44 @@ export async function doTokenRegistration(
|
|||||||
homeServer: window.mxMatrixClientPeg.get().getHomeserverUrl(),
|
homeServer: window.mxMatrixClientPeg.get().getHomeserverUrl(),
|
||||||
password: null,
|
password: null,
|
||||||
displayName: "Alice",
|
displayName: "Alice",
|
||||||
|
username: window.mxMatrixClientPeg.get().getUserIdLocalpart(),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Intercept calls to /sync and have them fail with a soft-logout
|
||||||
|
*
|
||||||
|
* Any further requests to /sync with the same access token are blocked.
|
||||||
|
*/
|
||||||
|
export async function interceptRequestsWithSoftLogout(page: Page, user: Credentials): Promise<void> {
|
||||||
|
await page.route("**/_matrix/client/*/sync*", async (route, req) => {
|
||||||
|
const accessToken = await req.headerValue("Authorization");
|
||||||
|
|
||||||
|
// now, if the access token on this request matches the expired one, block it
|
||||||
|
if (accessToken === `Bearer ${user.accessToken}`) {
|
||||||
|
console.log("Intercepting request with soft-logged-out access token");
|
||||||
|
await route.fulfill({
|
||||||
|
status: 401,
|
||||||
|
json: {
|
||||||
|
errcode: "M_UNKNOWN_TOKEN",
|
||||||
|
error: "Soft logout",
|
||||||
|
soft_logout: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise, pass through as normal
|
||||||
|
await route.continue();
|
||||||
|
});
|
||||||
|
|
||||||
|
const promise = page.waitForResponse((resp) => resp.url().includes("/sync") && resp.status() === 401);
|
||||||
|
|
||||||
|
// do something to make the active /sync return: create a new room
|
||||||
|
await page.evaluate(() => {
|
||||||
|
// don't wait for this to complete: it probably won't, because of the broken sync
|
||||||
|
window.mxMatrixClientPeg.get().createRoom({});
|
||||||
|
});
|
||||||
|
|
||||||
|
await promise;
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2024 The Matrix.org Foundation C.I.C.
|
Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -2,68 +2,14 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { API, Messages } from "mailhog";
|
import { API, Messages } from "mailhog";
|
||||||
import { Page } from "@playwright/test";
|
import { Page } from "@playwright/test";
|
||||||
|
|
||||||
import { test as base, expect } from "../../element-web-test";
|
import { expect } from "../../element-web-test";
|
||||||
import { MatrixAuthenticationService } from "../../plugins/matrix-authentication-service";
|
|
||||||
import { StartHomeserverOpts } from "../../plugins/homeserver";
|
|
||||||
|
|
||||||
export const test = base.extend<{
|
|
||||||
masPrepare: MatrixAuthenticationService;
|
|
||||||
mas: MatrixAuthenticationService;
|
|
||||||
}>({
|
|
||||||
// There's a bit of a chicken and egg problem between MAS & Synapse where they each need to know how to reach each other
|
|
||||||
// so spinning up a MAS is split into the prepare & start stage: prepare mas -> homeserver -> start mas to disentangle this.
|
|
||||||
masPrepare: async ({ context }, use) => {
|
|
||||||
const mas = new MatrixAuthenticationService(context);
|
|
||||||
await mas.prepare();
|
|
||||||
await use(mas);
|
|
||||||
},
|
|
||||||
mas: [
|
|
||||||
async ({ masPrepare: mas, homeserver, mailhog }, use, testInfo) => {
|
|
||||||
await mas.start(homeserver, mailhog.instance);
|
|
||||||
await use(mas);
|
|
||||||
await mas.stop(testInfo);
|
|
||||||
},
|
|
||||||
{ auto: true },
|
|
||||||
],
|
|
||||||
startHomeserverOpts: async ({ masPrepare }, use) => {
|
|
||||||
await use({
|
|
||||||
template: "mas-oidc",
|
|
||||||
variables: {
|
|
||||||
MAS_PORT: masPrepare.port,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
config: async ({ homeserver, startHomeserverOpts, context }, use) => {
|
|
||||||
const issuer = `http://localhost:${(startHomeserverOpts as StartHomeserverOpts).variables["MAS_PORT"]}/`;
|
|
||||||
const wellKnown = {
|
|
||||||
"m.homeserver": {
|
|
||||||
base_url: homeserver.config.baseUrl,
|
|
||||||
},
|
|
||||||
"org.matrix.msc2965.authentication": {
|
|
||||||
issuer,
|
|
||||||
account: `${issuer}account`,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Ensure org.matrix.msc2965.authentication is in well-known
|
|
||||||
await context.route("https://localhost/.well-known/matrix/client", async (route) => {
|
|
||||||
await route.fulfill({ json: wellKnown });
|
|
||||||
});
|
|
||||||
|
|
||||||
await use({
|
|
||||||
default_server_config: wellKnown,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export { expect };
|
|
||||||
|
|
||||||
export async function registerAccountMas(
|
export async function registerAccountMas(
|
||||||
page: Page,
|
page: Page,
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2024 New Vector Ltd.
|
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
|
||||||
Please see LICENSE files in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { test, expect, registerAccountMas } from ".";
|
|
||||||
import { isDendrite } from "../../plugins/homeserver/dendrite";
|
|
||||||
|
|
||||||
test.describe("OIDC Aware", () => {
|
|
||||||
test.skip(isDendrite, "does not yet support MAS");
|
|
||||||
test.slow(); // trace recording takes a while here
|
|
||||||
|
|
||||||
test("can register an account and manage it", async ({ context, page, homeserver, mailhog, app }) => {
|
|
||||||
await page.goto("/#/login");
|
|
||||||
await page.getByRole("button", { name: "Continue" }).click();
|
|
||||||
await registerAccountMas(page, mailhog.api, "alice", "alice@email.com", "Pa$sW0rD!");
|
|
||||||
|
|
||||||
// Eventually, we should end up at the home screen.
|
|
||||||
await expect(page).toHaveURL(/\/#\/home$/, { timeout: 10000 });
|
|
||||||
await expect(page.getByRole("heading", { name: "Welcome alice", exact: true })).toBeVisible();
|
|
||||||
|
|
||||||
// Open settings and navigate to account management
|
|
||||||
await app.settings.openUserSettings("Account");
|
|
||||||
const newPagePromise = context.waitForEvent("page");
|
|
||||||
await page.getByRole("button", { name: "Manage account" }).click();
|
|
||||||
|
|
||||||
// Assert new tab opened
|
|
||||||
const newPage = await newPagePromise;
|
|
||||||
await expect(newPage.getByText("Primary email")).toBeVisible();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -2,31 +2,30 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { test, expect, registerAccountMas } from ".";
|
import { test, expect } from "../../element-web-test.ts";
|
||||||
import { isDendrite } from "../../plugins/homeserver/dendrite";
|
import { registerAccountMas } from ".";
|
||||||
import { ElementAppPage } from "../../pages/ElementAppPage.ts";
|
import { ElementAppPage } from "../../pages/ElementAppPage.ts";
|
||||||
|
import { isDendrite } from "../../plugins/homeserver/dendrite";
|
||||||
|
import { masHomeserver } from "../../plugins/homeserver/synapse/masHomeserver.ts";
|
||||||
|
|
||||||
test.describe("OIDC Native", () => {
|
test.use(masHomeserver);
|
||||||
|
test.describe("OIDC Native", { tag: ["@no-firefox", "@no-webkit"] }, () => {
|
||||||
test.skip(isDendrite, "does not yet support MAS");
|
test.skip(isDendrite, "does not yet support MAS");
|
||||||
test.slow(); // trace recording takes a while here
|
test.slow(); // trace recording takes a while here
|
||||||
|
|
||||||
test.use({
|
test("can register the oauth2 client and an account", async ({ context, page, homeserver, mailhogClient, mas }) => {
|
||||||
labsFlags: ["feature_oidc_native_flow"],
|
const tokenUri = `${mas.baseUrl}/oauth2/token`;
|
||||||
});
|
|
||||||
|
|
||||||
test("can register the oauth2 client and an account", async ({ context, page, homeserver, mailhog, mas }) => {
|
|
||||||
const tokenUri = `http://localhost:${mas.port}/oauth2/token`;
|
|
||||||
const tokenApiPromise = page.waitForRequest(
|
const tokenApiPromise = page.waitForRequest(
|
||||||
(request) => request.url() === tokenUri && request.postDataJSON()["grant_type"] === "authorization_code",
|
(request) => request.url() === tokenUri && request.postDataJSON()["grant_type"] === "authorization_code",
|
||||||
);
|
);
|
||||||
|
|
||||||
await page.goto("/#/login");
|
await page.goto("/#/login");
|
||||||
await page.getByRole("button", { name: "Continue" }).click();
|
await page.getByRole("button", { name: "Continue" }).click();
|
||||||
await registerAccountMas(page, mailhog.api, "alice", "alice@email.com", "Pa$sW0rD!");
|
await registerAccountMas(page, mailhogClient, "alice", "alice@email.com", "Pa$sW0rD!");
|
||||||
|
|
||||||
// Eventually, we should end up at the home screen.
|
// Eventually, we should end up at the home screen.
|
||||||
await expect(page).toHaveURL(/\/#\/home$/, { timeout: 10000 });
|
await expect(page).toHaveURL(/\/#\/home$/, { timeout: 10000 });
|
||||||
@@ -45,15 +44,15 @@ test.describe("OIDC Native", () => {
|
|||||||
|
|
||||||
// Assert MAS sees the session as OIDC Native
|
// Assert MAS sees the session as OIDC Native
|
||||||
const newPage = await newPagePromise;
|
const newPage = await newPagePromise;
|
||||||
await newPage.getByText("Sessions").click();
|
await newPage.getByText("Devices").click();
|
||||||
await newPage.getByText(deviceId).click();
|
await newPage.getByText(deviceId).click();
|
||||||
await expect(newPage.getByText("Element")).toBeVisible();
|
await expect(newPage.getByText("Element")).toBeVisible();
|
||||||
await expect(newPage.getByText("oauth2_session:")).toBeVisible();
|
|
||||||
await expect(newPage.getByText("http://localhost:8080/")).toBeVisible();
|
await expect(newPage.getByText("http://localhost:8080/")).toBeVisible();
|
||||||
|
await expect(newPage).toHaveURL(/\/oauth2_session/);
|
||||||
await newPage.close();
|
await newPage.close();
|
||||||
|
|
||||||
// Assert logging out revokes both tokens
|
// Assert logging out revokes both tokens
|
||||||
const revokeUri = `http://localhost:${mas.port}/oauth2/revoke`;
|
const revokeUri = `${mas.baseUrl}/oauth2/revoke`;
|
||||||
const revokeAccessTokenPromise = page.waitForRequest(
|
const revokeAccessTokenPromise = page.waitForRequest(
|
||||||
(request) => request.url() === revokeUri && request.postDataJSON()["token_type_hint"] === "access_token",
|
(request) => request.url() === revokeUri && request.postDataJSON()["token_type_hint"] === "access_token",
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ Copyright 2024 New Vector Ltd.
|
|||||||
Copyright 2023 Ahmad Kadri
|
Copyright 2023 Ahmad Kadri
|
||||||
Copyright 2023 Nordeck IT + Consulting GmbH.
|
Copyright 2023 Nordeck IT + Consulting GmbH.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Copyright 2024 New Vector Ltd.
|
* Copyright 2024 New Vector Ltd.
|
||||||
* Copyright 2024 The Matrix.org Foundation C.I.C.
|
* Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -13,6 +13,8 @@ import { Client } from "../../pages/client";
|
|||||||
import { ElementAppPage } from "../../pages/ElementAppPage";
|
import { ElementAppPage } from "../../pages/ElementAppPage";
|
||||||
import { Bot } from "../../pages/bot";
|
import { Bot } from "../../pages/bot";
|
||||||
|
|
||||||
|
type RoomRef = { name: string; roomId: string };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set up for pinned message tests.
|
* Set up for pinned message tests.
|
||||||
*/
|
*/
|
||||||
@@ -47,7 +49,7 @@ export class Helpers {
|
|||||||
* @param room - the name of the room to send messages into
|
* @param room - the name of the room to send messages into
|
||||||
* @param messages - the list of messages to send, these can be strings or implementations of MessageSpec like `editOf`
|
* @param messages - the list of messages to send, these can be strings or implementations of MessageSpec like `editOf`
|
||||||
*/
|
*/
|
||||||
async receiveMessages(room: string | { name: string }, messages: string[]) {
|
async receiveMessages(room: RoomRef, messages: string[]) {
|
||||||
await this.sendMessageAsClient(this.bot, room, messages);
|
await this.sendMessageAsClient(this.bot, room, messages);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,9 +57,8 @@ export class Helpers {
|
|||||||
* Use the supplied client to send messages or perform actions as specified by
|
* Use the supplied client to send messages or perform actions as specified by
|
||||||
* the supplied {@link Message} items.
|
* the supplied {@link Message} items.
|
||||||
*/
|
*/
|
||||||
private async sendMessageAsClient(cli: Client, roomName: string | { name: string }, messages: string[]) {
|
private async sendMessageAsClient(cli: Client, room: RoomRef, messages: string[]) {
|
||||||
const room = await this.findRoomByName(typeof roomName === "string" ? roomName : roomName.name);
|
const roomId = room.roomId;
|
||||||
const roomId = await room.evaluate((room) => room.roomId);
|
|
||||||
|
|
||||||
for (const message of messages) {
|
for (const message of messages) {
|
||||||
await cli.sendMessage(roomId, { body: message, msgtype: "m.text" });
|
await cli.sendMessage(roomId, { body: message, msgtype: "m.text" });
|
||||||
@@ -73,22 +74,11 @@ export class Helpers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Find a room by its name
|
|
||||||
* @param roomName
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
private async findRoomByName(roomName: string) {
|
|
||||||
return this.app.client.evaluateHandle((cli, roomName) => {
|
|
||||||
return cli.getRooms().find((r) => r.name === roomName);
|
|
||||||
}, roomName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open the room with the supplied name.
|
* Open the room with the supplied name.
|
||||||
*/
|
*/
|
||||||
async goTo(room: string | { name: string }) {
|
async goTo(room: RoomRef) {
|
||||||
await this.app.viewRoomByName(typeof room === "string" ? room : room.name);
|
await this.app.viewRoomByName(room.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Copyright 2024 New Vector Ltd.
|
* Copyright 2024 New Vector Ltd.
|
||||||
* Copyright 2024 The Matrix.org Foundation C.I.C.
|
* Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
import { test, expect } from "../../element-web-test";
|
import { test, expect } from "../../element-web-test";
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -11,6 +11,8 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
import { customEvent, many, test } from ".";
|
import { customEvent, many, test } from ".";
|
||||||
|
|
||||||
test.describe("Read receipts", { tag: "@mergequeue" }, () => {
|
test.describe("Read receipts", { tag: "@mergequeue" }, () => {
|
||||||
|
test.slow();
|
||||||
|
|
||||||
test.describe("Ignored events", () => {
|
test.describe("Ignored events", () => {
|
||||||
test("If all events after receipt are unimportant, the room is read", async ({
|
test("If all events after receipt are unimportant, the room is read", async ({
|
||||||
roomAlpha: room1,
|
roomAlpha: room1,
|
||||||
@@ -120,7 +122,7 @@ test.describe("Read receipts", { tag: "@mergequeue" }, () => {
|
|||||||
await util.assertUnread(room2, 40);
|
await util.assertUnread(room2, 40);
|
||||||
|
|
||||||
// When I jump to a message in the middle and page up
|
// When I jump to a message in the middle and page up
|
||||||
await msg.jumpTo(room2.name, "x\ny\nz\nMsg0020");
|
await msg.jumpTo(room2, "x\ny\nz\nMsg0020");
|
||||||
await util.pageUp();
|
await util.pageUp();
|
||||||
|
|
||||||
// Then the room is still unread
|
// Then the room is still unread
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -13,6 +13,8 @@ import { Bot } from "../../pages/bot";
|
|||||||
import { Client } from "../../pages/client";
|
import { Client } from "../../pages/client";
|
||||||
import { ElementAppPage } from "../../pages/ElementAppPage";
|
import { ElementAppPage } from "../../pages/ElementAppPage";
|
||||||
|
|
||||||
|
type RoomRef = { name: string; roomId: string };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set up for a read receipt test:
|
* Set up for a read receipt test:
|
||||||
* - Create a user with the supplied name
|
* - Create a user with the supplied name
|
||||||
@@ -22,9 +24,9 @@ import { ElementAppPage } from "../../pages/ElementAppPage";
|
|||||||
*/
|
*/
|
||||||
export const test = base.extend<{
|
export const test = base.extend<{
|
||||||
roomAlphaName?: string;
|
roomAlphaName?: string;
|
||||||
roomAlpha: { name: string; roomId: string };
|
roomAlpha: RoomRef;
|
||||||
roomBetaName?: string;
|
roomBetaName?: string;
|
||||||
roomBeta: { name: string; roomId: string };
|
roomBeta: RoomRef;
|
||||||
msg: MessageBuilder;
|
msg: MessageBuilder;
|
||||||
util: Helpers;
|
util: Helpers;
|
||||||
}>({
|
}>({
|
||||||
@@ -248,12 +250,13 @@ export class MessageBuilder {
|
|||||||
/**
|
/**
|
||||||
* Find and display a message.
|
* Find and display a message.
|
||||||
*
|
*
|
||||||
* @param roomName the name of the room to look inside
|
* @param roomRef the ref of the room to look inside
|
||||||
* @param message the content of the message to fine
|
* @param message the content of the message to fine
|
||||||
* @param includeThreads look for messages inside threads, not just the main timeline
|
* @param includeThreads look for messages inside threads, not just the main timeline
|
||||||
*/
|
*/
|
||||||
async jumpTo(roomName: string, message: string, includeThreads = false) {
|
async jumpTo(roomRef: RoomRef, message: string, includeThreads = false) {
|
||||||
const room = await this.helpers.findRoomByName(roomName);
|
const room = await this.helpers.findRoomById(roomRef.roomId);
|
||||||
|
expect(room).toBeTruthy();
|
||||||
const foundMessage = await this.getMessage(room, message, includeThreads);
|
const foundMessage = await this.getMessage(room, message, includeThreads);
|
||||||
const roomId = await room.evaluate((room) => room.roomId);
|
const roomId = await room.evaluate((room) => room.roomId);
|
||||||
const foundMessageId = await foundMessage.evaluate((ev) => ev.getId());
|
const foundMessageId = await foundMessage.evaluate((ev) => ev.getId());
|
||||||
@@ -333,9 +336,10 @@ class Helpers {
|
|||||||
* Use the supplied client to send messages or perform actions as specified by
|
* Use the supplied client to send messages or perform actions as specified by
|
||||||
* the supplied {@link Message} items.
|
* the supplied {@link Message} items.
|
||||||
*/
|
*/
|
||||||
async sendMessageAsClient(cli: Client, roomName: string | { name: string }, messages: Message[]) {
|
async sendMessageAsClient(cli: Client, roomRef: RoomRef, messages: Message[]) {
|
||||||
const room = await this.findRoomByName(typeof roomName === "string" ? roomName : roomName.name);
|
const roomId = roomRef.roomId;
|
||||||
const roomId = await room.evaluate((room) => room.roomId);
|
const room = await this.findRoomById(roomId);
|
||||||
|
expect(room).toBeTruthy();
|
||||||
|
|
||||||
for (const message of messages) {
|
for (const message of messages) {
|
||||||
if (typeof message === "string") {
|
if (typeof message === "string") {
|
||||||
@@ -359,7 +363,7 @@ class Helpers {
|
|||||||
/**
|
/**
|
||||||
* Open the room with the supplied name.
|
* Open the room with the supplied name.
|
||||||
*/
|
*/
|
||||||
async goTo(room: string | { name: string }) {
|
async goTo(room: RoomRef) {
|
||||||
await this.app.viewRoomByName(typeof room === "string" ? room : room.name);
|
await this.app.viewRoomByName(typeof room === "string" ? room : room.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -423,17 +427,16 @@ class Helpers {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getRoomListTile(room: string | { name: string }) {
|
getRoomListTile(label: string) {
|
||||||
const roomName = typeof room === "string" ? room : room.name;
|
return this.page.getByRole("treeitem", { name: new RegExp("^" + label) });
|
||||||
return this.page.getByRole("treeitem", { name: new RegExp("^" + roomName) });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Click the "Mark as Read" context menu item on the room with the supplied name
|
* Click the "Mark as Read" context menu item on the room with the supplied name
|
||||||
* in the room list.
|
* in the room list.
|
||||||
*/
|
*/
|
||||||
async markAsRead(room: string | { name: string }) {
|
async markAsRead(room: RoomRef) {
|
||||||
await this.getRoomListTile(room).click({ button: "right" });
|
await this.getRoomListTile(room.name).click({ button: "right" });
|
||||||
await this.page.getByText("Mark as read").click();
|
await this.page.getByText("Mark as read").click();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -441,8 +444,8 @@ class Helpers {
|
|||||||
* Assert that the room with the supplied name is "read" in the room list - i.g.
|
* Assert that the room with the supplied name is "read" in the room list - i.g.
|
||||||
* has not dot or count of unread messages.
|
* has not dot or count of unread messages.
|
||||||
*/
|
*/
|
||||||
async assertRead(room: string | { name: string }) {
|
async assertRead(room: RoomRef) {
|
||||||
const tile = this.getRoomListTile(room);
|
const tile = this.getRoomListTile(room.name);
|
||||||
await expect(tile.locator(".mx_NotificationBadge_dot")).not.toBeVisible();
|
await expect(tile.locator(".mx_NotificationBadge_dot")).not.toBeVisible();
|
||||||
await expect(tile.locator(".mx_NotificationBadge_count")).not.toBeVisible();
|
await expect(tile.locator(".mx_NotificationBadge_count")).not.toBeVisible();
|
||||||
}
|
}
|
||||||
@@ -452,7 +455,7 @@ class Helpers {
|
|||||||
* (In practice, this just waits a short while to allow any unread marker to
|
* (In practice, this just waits a short while to allow any unread marker to
|
||||||
* appear, and then asserts that the room is read.)
|
* appear, and then asserts that the room is read.)
|
||||||
*/
|
*/
|
||||||
async assertStillRead(room: string | { name: string }) {
|
async assertStillRead(room: RoomRef) {
|
||||||
await this.page.waitForTimeout(200);
|
await this.page.waitForTimeout(200);
|
||||||
await this.assertRead(room);
|
await this.assertRead(room);
|
||||||
}
|
}
|
||||||
@@ -462,8 +465,8 @@ class Helpers {
|
|||||||
* @param room - the name of the room to check
|
* @param room - the name of the room to check
|
||||||
* @param count - the numeric count to assert, or if "." specified then a bold/dot (no count) state is asserted
|
* @param count - the numeric count to assert, or if "." specified then a bold/dot (no count) state is asserted
|
||||||
*/
|
*/
|
||||||
async assertUnread(room: string | { name: string }, count: number | ".") {
|
async assertUnread(room: RoomRef, count: number | ".") {
|
||||||
const tile = this.getRoomListTile(room);
|
const tile = this.getRoomListTile(room.name);
|
||||||
if (count === ".") {
|
if (count === ".") {
|
||||||
await expect(tile.locator(".mx_NotificationBadge_dot")).toBeVisible();
|
await expect(tile.locator(".mx_NotificationBadge_dot")).toBeVisible();
|
||||||
} else {
|
} else {
|
||||||
@@ -478,8 +481,8 @@ class Helpers {
|
|||||||
* @param room - the name of the room to check
|
* @param room - the name of the room to check
|
||||||
* @param lessThan - the number of unread messages that is too many
|
* @param lessThan - the number of unread messages that is too many
|
||||||
*/
|
*/
|
||||||
async assertUnreadLessThan(room: string | { name: string }, lessThan: number) {
|
async assertUnreadLessThan(room: RoomRef, lessThan: number) {
|
||||||
const tile = this.getRoomListTile(room);
|
const tile = this.getRoomListTile(room.name);
|
||||||
// https://playwright.dev/docs/test-assertions#expectpoll
|
// https://playwright.dev/docs/test-assertions#expectpoll
|
||||||
// .toBeLessThan doesn't have a retry mechanism, so we use .poll
|
// .toBeLessThan doesn't have a retry mechanism, so we use .poll
|
||||||
await expect
|
await expect
|
||||||
@@ -496,8 +499,8 @@ class Helpers {
|
|||||||
* @param room - the name of the room to check
|
* @param room - the name of the room to check
|
||||||
* @param greaterThan - the number of unread messages that is too few
|
* @param greaterThan - the number of unread messages that is too few
|
||||||
*/
|
*/
|
||||||
async assertUnreadGreaterThan(room: string | { name: string }, greaterThan: number) {
|
async assertUnreadGreaterThan(room: RoomRef, greaterThan: number) {
|
||||||
const tile = this.getRoomListTile(room);
|
const tile = this.getRoomListTile(room.name);
|
||||||
// https://playwright.dev/docs/test-assertions#expectpoll
|
// https://playwright.dev/docs/test-assertions#expectpoll
|
||||||
// .toBeGreaterThan doesn't have a retry mechanism, so we use .poll
|
// .toBeGreaterThan doesn't have a retry mechanism, so we use .poll
|
||||||
await expect
|
await expect
|
||||||
@@ -531,10 +534,10 @@ class Helpers {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async findRoomByName(roomName: string): Promise<JSHandle<Room>> {
|
async findRoomById(roomId: string): Promise<JSHandle<Room>> {
|
||||||
return this.app.client.evaluateHandle((cli, roomName) => {
|
return this.app.client.evaluateHandle((cli, roomId) => {
|
||||||
return cli.getRooms().find((r) => r.name === roomName);
|
return cli.getRooms().find((r) => r.roomId === roomId);
|
||||||
}, roomName);
|
}, roomId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getThreadListTile(rootMessage: string) {
|
private async getThreadListTile(rootMessage: string) {
|
||||||
@@ -578,7 +581,7 @@ class Helpers {
|
|||||||
* @param room - the name of the room to send messages into
|
* @param room - the name of the room to send messages into
|
||||||
* @param messages - the list of messages to send, these can be strings or implementations of MessageSpec like `editOf`
|
* @param messages - the list of messages to send, these can be strings or implementations of MessageSpec like `editOf`
|
||||||
*/
|
*/
|
||||||
async receiveMessages(room: string | { name: string }, messages: Message[]) {
|
async receiveMessages(room: RoomRef, messages: Message[]) {
|
||||||
await this.sendMessageAsClient(this.bot, room, messages);
|
await this.sendMessageAsClient(this.bot, room, messages);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -101,7 +101,7 @@ test.describe("Read receipts", { tag: "@mergequeue" }, () => {
|
|||||||
await util.goTo(room1);
|
await util.goTo(room1);
|
||||||
|
|
||||||
// When I read an older message in the thread
|
// When I read an older message in the thread
|
||||||
await msg.jumpTo(room2.name, "InThread0000", true);
|
await msg.jumpTo(room2, "InThread0000", true);
|
||||||
|
|
||||||
// Then the thread is still marked as unread
|
// Then the thread is still marked as unread
|
||||||
await util.backToThreadsList();
|
await util.backToThreadsList();
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ test.describe("Read receipts", { tag: "@mergequeue" }, () => {
|
|||||||
await util.assertUnread(room2, 30);
|
await util.assertUnread(room2, 30);
|
||||||
|
|
||||||
// When I jump to one of the older messages
|
// When I jump to one of the older messages
|
||||||
await msg.jumpTo(room2.name, "Msg0001");
|
await msg.jumpTo(room2, "Msg0001");
|
||||||
|
|
||||||
// Then the room is still unread, but some messages were read
|
// Then the room is still unread, but some messages were read
|
||||||
await util.assertUnreadLessThan(room2, 30);
|
await util.assertUnreadLessThan(room2, 30);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ test.describe("Read receipts", { tag: "@mergequeue" }, () => {
|
|||||||
await util.assertUnread(room2, 61); // Sanity
|
await util.assertUnread(room2, 61); // Sanity
|
||||||
|
|
||||||
// When I jump to an old message and read the thread
|
// When I jump to an old message and read the thread
|
||||||
await msg.jumpTo(room2.name, "beforeThread0000");
|
await msg.jumpTo(room2, "beforeThread0000");
|
||||||
// When the thread is opened, the timeline is scrolled until the thread root reached the center
|
// When the thread is opened, the timeline is scrolled until the thread root reached the center
|
||||||
await util.openThread("ThreadRoot");
|
await util.openThread("ThreadRoot");
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user