Consolidate Build & Test CI (#32929)

* Consolidate Build & Test CI

* Add missing workflow dependency

* Fix artifact name clash

* Fix playwright config

* Fix playwright_ew job

* Fix ed tests

* Fix playwright tags

* Iterate

* Fix file reads

* Fix sample-files paths

* Fix PW_TAG

* Fix blob report paths

* Delint

* Fix build-and-test.yaml

* Iterate

* Fix consentHomeserver.ts

* Simplify

* Iterate

* Delint

* Iterate

* Iterate

* Iterate

* Specify shell

* Simplify

* Delete apps/web/playwright/sample-files/index.ts

* Discard changes to apps/web/playwright/sample-files/index.ts

* Exclude playwright-common from coverage gate

* Attempt to speed up arm64 desktop test

* Revert "Attempt to speed up arm64 desktop test"

This reverts commit 8fa8ff0c785da6dad05bda938c8af24fa6af0451.

* Iterate

* Fix cache key

* Accept python or python3 as per node-gyp

* Accept python or python3 as per node-gypd

* Exclude apps/desktop/hak from coverage gate
This commit is contained in:
Michael Telatynski
2026-03-31 12:52:50 +00:00
committed by GitHub
parent cabac4ef0e
commit 2b3720b4a2
18 changed files with 203 additions and 254 deletions
@@ -5,7 +5,7 @@ on:
# Privilege escalation necessary to publish to Netlify # Privilege escalation necessary to publish to Netlify
# 🚨 We must not execute any checked out code here. # 🚨 We must not execute any checked out code here.
workflow_run: # zizmor: ignore[dangerous-triggers] workflow_run: # zizmor: ignore[dangerous-triggers]
workflows: ["End to End Tests"] workflows: ["Build & Test"]
types: types:
- completed - completed
@@ -1,7 +1,13 @@
# Produce a build of element-web with this version of react-sdk # builds Element Web
# and any matching branches of element-web and js-sdk, output it # runs Playwright tests against the built Element Web
# as an artifact and run end-to-end tests. # builds Element Desktop using the built Element Web
name: End to End Tests #
# Tries to use a matching js-sdk branch for the build.
#
# Produces a `webapp` artifact
# Produces multiple Desktop artifacts
# Produces multiple Playwright report artifacts
name: Build & Test
on: on:
# CRON to run all Projects at 6am UTC # CRON to run all Projects at 6am UTC
schedule: schedule:
@@ -10,7 +16,8 @@ on:
merge_group: merge_group:
types: [checks_requested] types: [checks_requested]
push: push:
branches: [develop, master] # We do not build on push to develop as the merge_group check handles that
branches: [staging, master]
repository_dispatch: repository_dispatch:
types: [element-web-notify] types: [element-web-notify]
@@ -35,15 +42,15 @@ 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) # Use 4 runners in the default case, but only 1 when running on a schedule where we run all 5 projects
NUM_RUNNERS: ${{ github.event_name == 'schedule' && 4 || 6 }} NUM_RUNNERS: ${{ github.event_name == 'schedule' && 1 || 4 }}
NX_DEFAULT_OUTPUT_STYLE: stream-without-prefixes NX_DEFAULT_OUTPUT_STYLE: stream-without-prefixes
permissions: {} # No permissions required permissions: {} # No permissions required
jobs: jobs:
build: build_ew:
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: outputs:
@@ -94,9 +101,9 @@ jobs:
const matrix = Array.from({ length: numRunners }, (_, i) => i + 1); const matrix = Array.from({ length: numRunners }, (_, i) => i + 1);
core.setOutput("matrix", JSON.stringify(matrix)); core.setOutput("matrix", JSON.stringify(matrix));
playwright: playwright_ew:
name: "Run Tests [${{ matrix.project }}] ${{ matrix.runner }}/${{ needs.build.outputs.num-runners }}" name: "Run Tests [${{ matrix.project }}] ${{ matrix.runner }}/${{ needs.build_ew.outputs.num-runners }}"
needs: build needs: build_ew
if: inputs.skip != true if: inputs.skip != true
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
permissions: permissions:
@@ -107,7 +114,7 @@ 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: ${{ fromJSON(needs.build.outputs.runners-matrix) }} runner: ${{ fromJSON(needs.build_ew.outputs.runners-matrix) }}
project: project:
- Chrome - Chrome
- Firefox - Firefox
@@ -179,29 +186,85 @@ jobs:
--project="${{ matrix.project }}" \ --project="${{ matrix.project }}" \
${{ (github.event_name == 'pull_request' && matrix.runAllTests == false ) && '--grep-invert @mergequeue' || '' }} ${{ (github.event_name == 'pull_request' && matrix.runAllTests == false ) && '--grep-invert @mergequeue' || '' }}
env: env:
SHARD: ${{ format('{0}/{1}', matrix.runner, needs.build.outputs.num-runners) }} SHARD: ${{ format('{0}/{1}', matrix.runner, needs.build_ew.outputs.num-runners) }}
- name: Upload blob report to GitHub Actions Artifacts - name: Upload blob report to GitHub Actions Artifacts
if: always() if: always()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
with: with:
name: all-blob-reports-${{ matrix.project }}-${{ matrix.runner }} name: blob-report-${{ matrix.project }}-${{ matrix.runner }}
path: apps/web/blob-report path: apps/web/blob-report
retention-days: 1 retention-days: 1
if-no-files-found: error
downstream-modules: downstream-modules:
name: Downstream Playwright tests [element-modules] name: Downstream Playwright tests [element-modules]
needs: build needs: build_ew
if: inputs.skip != true && github.event_name == 'merge_group' if: inputs.skip != true && github.event_name == 'merge_group'
uses: element-hq/element-modules/.github/workflows/reusable-playwright-tests.yml@main # zizmor: ignore[unpinned-uses] uses: element-hq/element-modules/.github/workflows/reusable-playwright-tests.yml@main # zizmor: ignore[unpinned-uses]
with: with:
webapp-artifact: webapp webapp-artifact: webapp
prepare_ed:
name: "Prepare Element Desktop"
uses: ./.github/workflows/build_desktop_prepare.yaml
needs: build_ew
if: inputs.skip != true
permissions:
contents: read
with:
config: ${{ (github.event.pull_request.base.ref || github.ref_name) == 'develop' && 'element.io/nightly' || 'element.io/release' }}
version: ${{ (github.event.pull_request.base.ref || github.ref_name) == 'develop' && 'develop' || '' }}
webapp-artifact: webapp
build_ed_windows:
needs: prepare_ed
name: "Desktop Windows"
uses: ./.github/workflows/build_desktop_windows.yaml
if: inputs.skip != true
strategy:
matrix:
arch: [x64, ia32, arm64]
with:
arch: ${{ matrix.arch }}
blob_report: true
build_ed_linux:
needs: prepare_ed
name: "Desktop Linux"
uses: ./.github/workflows/build_desktop_linux.yaml
if: inputs.skip != true
strategy:
matrix:
sqlcipher: [system, static]
arch: [amd64, arm64]
runAllTests:
- ${{ github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'X-Run-All-Tests') }}
# We ship static sqlcipher builds, so delegate testing the system builds to the merge queue
exclude:
- runAllTests: false
sqlcipher: system
with:
sqlcipher: ${{ matrix.sqlcipher }}
arch: ${{ matrix.arch }}
blob_report: true
build_ed_macos:
needs: prepare_ed
name: "Desktop macOS"
uses: ./.github/workflows/build_desktop_macos.yaml
if: inputs.skip != true
with:
blob_report: true
complete: complete:
name: end-to-end-tests name: end-to-end-tests
needs: needs:
- playwright - playwright_ew
- downstream-modules - downstream-modules
- build_ed_windows
- build_ed_linux
- build_ed_macos
if: always() if: always()
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
@@ -227,18 +290,20 @@ jobs:
if: inputs.skip != true if: inputs.skip != true
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with: with:
pattern: all-blob-reports-* pattern: blob-report-*
path: apps/web/all-blob-reports path: all-blob-reports
merge-multiple: true merge-multiple: true
- name: Merge into HTML Report - name: Merge into HTML Report
if: inputs.skip != true if: inputs.skip != true
working-directory: apps/web run: |
run: pnpm playwright merge-reports --reporter=html,./playwright/flaky-reporter.ts,@element-hq/element-web-playwright-common/lib/stale-screenshot-reporter.js ./all-blob-reports pnpm playwright merge-reports \
--config=playwright-merge.config.ts \
./all-blob-reports
env: env:
# Only pass creds to the flaky-reporter on main branch runs # Only pass creds to the flaky-reporter on main branch runs
GITHUB_TOKEN: ${{ github.ref_name == 'develop' && secrets.ELEMENT_BOT_TOKEN || '' }} GITHUB_TOKEN: ${{ github.ref_name == 'develop' && secrets.ELEMENT_BOT_TOKEN || '' }}
PLAYWRIGHT_HTML_TITLE: ${{ case(github.event_name == 'pull_request', format('EW Playwright Report PR-{0}', env.PR_NUMBER), 'EW Playwright Report') }} PLAYWRIGHT_HTML_TITLE: ${{ case(github.event_name == 'pull_request', format('Playwright Report PR-{0}', env.PR_NUMBER), 'Playwright Report') }}
# Upload the HTML report even if one of our reporters fails, this can happen when stale screenshots are detected # Upload the HTML report even if one of our reporters fails, this can happen when stale screenshots are detected
- name: Upload HTML report - name: Upload HTML report
@@ -246,7 +311,7 @@ jobs:
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
with: with:
name: html-report name: html-report
path: apps/web/playwright-report path: playwright-report
retention-days: 14 retention-days: 14
if-no-files-found: error if-no-files-found: error
@@ -1,89 +0,0 @@
name: Build and Test
on:
pull_request: {}
push:
branches: [develop, staging, master]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions: {} # No permissions required
jobs:
fetch:
uses: ./.github/workflows/build_desktop_prepare.yaml
permissions:
contents: read
with:
config: ${{ (github.event.pull_request.base.ref || github.ref_name) == 'develop' && 'element.io/nightly' || 'element.io/release' }}
version: ${{ (github.event.pull_request.base.ref || github.ref_name) == 'develop' && 'develop' || '' }}
branch-matching: true
windows:
needs: fetch
name: Windows
uses: ./.github/workflows/build_desktop_windows.yaml
strategy:
matrix:
arch: [x64, ia32, arm64]
with:
arch: ${{ matrix.arch }}
blob_report: true
linux:
needs: fetch
name: "Linux (${{ matrix.arch }}) (sqlcipher: ${{ matrix.sqlcipher }})"
uses: ./.github/workflows/build_desktop_linux.yaml
strategy:
matrix:
sqlcipher: [system, static]
arch: [amd64, arm64]
with:
sqlcipher: ${{ matrix.sqlcipher }}
arch: ${{ matrix.arch }}
blob_report: true
macos:
needs: fetch
name: macOS
uses: ./.github/workflows/build_desktop_macos.yaml
with:
blob_report: true
tests-done:
needs: [windows, linux, macos]
runs-on: ubuntu-24.04
if: ${{ !cancelled() }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
with:
cache: "pnpm"
node-version: "lts/*"
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Download blob reports from GitHub Actions Artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
pattern: blob-report-*
path: apps/desktop/all-blob-reports
merge-multiple: true
- name: Merge into HTML Report
working-directory: apps/desktop
run: pnpm playwright merge-reports -c ./playwright.config.ts --reporter=html ./all-blob-reports
- name: Upload HTML report
if: always()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
with:
name: html-report
path: apps/desktop/playwright-report
retention-days: 14
- if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
run: exit 1
+5 -19
View File
@@ -28,7 +28,7 @@ on:
type: string type: string
required: false required: false
description: | description: |
The name of the prepare artifact to use, defaults to 'webapp'. The name of the prepare artifact to use, defaults to 'desktop-prepare'.
The artifact must contain the following: The artifact must contain the following:
+ webapp.asar - the asar archive of the webapp to embed in the desktop app + webapp.asar - the asar archive of the webapp to embed in the desktop app
+ electronVersion - the version of electron to use for cache keying + electronVersion - the version of electron to use for cache keying
@@ -38,7 +38,7 @@ on:
The artifact can also contain any additional files which will be applied as overrides to the checkout root before building, The artifact can also contain any additional files which will be applied as overrides to the checkout root before building,
for example icons in the `build/` directory to override the app icons. for example icons in the `build/` directory to override the app icons.
default: "webapp" default: "desktop-prepare"
test: test:
type: boolean type: boolean
required: false required: false
@@ -73,20 +73,8 @@ jobs:
# https://github.com/matrix-org/seshat/issues/135 # https://github.com/matrix-org/seshat/issues/135
runs-on: ${{ inputs.runs-on || (inputs.arch == 'arm64' && 'ubuntu-22.04-arm' || 'ubuntu-22.04') }} runs-on: ${{ inputs.runs-on || (inputs.arch == 'arm64' && 'ubuntu-22.04-arm' || 'ubuntu-22.04') }}
env: env:
HAK_DOCKER_IMAGE: ghcr.io/element-hq/element-web/desktop-build-env HAK_DOCKER_IMAGE: ghcr.io/element-hq/element-web/desktop-build-env:${{ case(github.event_name == 'push', inputs.ref || github.ref_name, github.event_name == 'release', 'staging', 'develop') }}
steps: steps:
- name: Resolve docker image tag for push
if: github.event_name == 'push'
run: echo "HAK_DOCKER_IMAGE=$HAK_DOCKER_IMAGE:$REF" >> $GITHUB_ENV
env:
REF: ${{ inputs.ref || github.ref_name }}
- name: Resolve docker image tag for release
if: github.event_name == 'release'
run: echo "HAK_DOCKER_IMAGE=$HAK_DOCKER_IMAGE:staging" >> $GITHUB_ENV
- name: Resolve docker image tag for other triggers
if: github.event_name != 'push' && github.event_name != 'release'
run: echo "HAK_DOCKER_IMAGE=$HAK_DOCKER_IMAGE:develop" >> $GITHUB_ENV
- uses: nbucic/variable-mapper@0673f6891a0619ba7c002ecfed0f9f4f39017b6f - uses: nbucic/variable-mapper@0673f6891a0619ba7c002ecfed0f9f4f39017b6f
id: config id: config
with: with:
@@ -95,11 +83,9 @@ jobs:
map: | map: |
{ {
"amd64": { "amd64": {
"target": "x86_64-unknown-linux-gnu",
"arch": "x86-64" "arch": "x86-64"
}, },
"arm64": { "arm64": {
"target": "aarch64-unknown-linux-gnu",
"arch": "aarch64", "arch": "aarch64",
"build-args": "--arm64" "build-args": "--arm64"
} }
@@ -120,7 +106,7 @@ jobs:
id: cache id: cache
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5 uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with: with:
key: ${{ runner.os }}-${{ github.ref_name }}-${{ inputs.sqlcipher }}-${{ inputs.arch }}-${{ hashFiles('hakHash', 'electronVersion', 'dockerbuild/*') }} key: ${{ runner.os }}-${{ github.ref_name }}-${{ inputs.sqlcipher }}-${{ inputs.arch }}-${{ hashFiles('apps/desktop/hakHash', 'apps/desktop/electronVersion', 'apps/desktop/dockerbuild/*') }}
path: | path: |
apps/desktop/.hak apps/desktop/.hak
@@ -135,7 +121,7 @@ jobs:
- name: Install Deps - name: Install Deps
working-directory: apps/desktop working-directory: apps/desktop
run: pnpm install --frozen-lockfile run: "pnpm install --frozen-lockfile --filter element-desktop"
- name: "Get modified files" - name: "Get modified files"
id: changed_files id: changed_files
+4 -4
View File
@@ -37,7 +37,7 @@ on:
type: string type: string
required: false required: false
description: | description: |
The name of the prepare artifact to use, defaults to 'webapp'. The name of the prepare artifact to use, defaults to 'desktop-prepare'.
The artifact must contain the following: The artifact must contain the following:
+ webapp.asar - the asar archive of the webapp to embed in the desktop app + webapp.asar - the asar archive of the webapp to embed in the desktop app
+ electronVersion - the version of electron to use for cache keying + electronVersion - the version of electron to use for cache keying
@@ -46,7 +46,7 @@ on:
The artifact can also contain any additional files which will be applied as overrides to the checkout root before building, The artifact can also contain any additional files which will be applied as overrides to the checkout root before building,
for example icons in the `build/` directory to override the app icons. for example icons in the `build/` directory to override the app icons.
default: "webapp" default: "desktop-prepare"
test: test:
type: boolean type: boolean
required: false required: false
@@ -92,7 +92,7 @@ jobs:
id: cache id: cache
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5 uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with: with:
key: ${{ runner.os }}-${{ hashFiles('hakHash', 'electronVersion') }} key: ${{ runner.os }}-${{ hashFiles('apps/desktop/hakHash', 'apps/desktop/electronVersion') }}
path: | path: |
apps/desktop/.hak apps/desktop/.hak
@@ -121,7 +121,7 @@ jobs:
- name: Install Deps - name: Install Deps
working-directory: apps/desktop working-directory: apps/desktop
run: "pnpm install --frozen-lockfile" run: "pnpm install --frozen-lockfile --filter element-desktop"
- name: Build Natives - name: Build Natives
if: steps.cache.outputs.cache-hit != 'true' if: steps.cache.outputs.cache-hit != 'true'
+16 -19
View File
@@ -20,11 +20,10 @@ on:
required: false required: false
default: false default: false
description: "Whether the build should be deployed to production" description: "Whether the build should be deployed to production"
branch-matching: webapp-artifact:
type: boolean type: string
required: false required: false
default: false description: "Name of the webapp artifact that should be used, will fetch a relevant build if omitted"
description: "Whether the branch name should be matched to find the element-web commit"
secrets: secrets:
# Required if `nightly` is set # Required if `nightly` is set
CF_R2_ACCESS_KEY_ID: CF_R2_ACCESS_KEY_ID:
@@ -66,28 +65,26 @@ jobs:
- name: Install Deps - name: Install Deps
working-directory: apps/desktop working-directory: apps/desktop
run: "pnpm install --frozen-lockfile" run: "pnpm install --frozen-lockfile --filter element-desktop"
- name: Fetch Element Web (matching branch) - name: Fetch Element Web (from artifact)
id: branch-matching if: inputs.webapp-artifact != ''
if: inputs.branch-matching uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: ${{ inputs.webapp-artifact }}
path: apps/desktop/webapp
- name: Build webapp.asar (from artifact)
if: inputs.webapp-artifact != ''
working-directory: apps/desktop working-directory: apps/desktop
continue-on-error: true
run: | run: |
scripts/branch-match.sh cp -f "$CONFIG_DIR/config.json" webapp/config.json
cp "$CONFIG_DIR/config.json" element-web/
pnpm --cwd element-web install --frozen-lockfile
pnpm --cwd element-web run build
mv element-web/webapp .
pnpm run asar-webapp pnpm run asar-webapp
env: env:
# These must be set for branch-match.sh to get the right branch
REPOSITORY: ${{ github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number }}
CONFIG_DIR: ${{ inputs.config }} CONFIG_DIR: ${{ inputs.config }}
- name: Fetch Element Web (${{ inputs.version }}) - name: Fetch Element Web (${{ inputs.version }})
if: steps.branch-matching.outcome == 'failure' || steps.branch-matching.outcome == 'skipped' if: inputs.webapp-artifact == ''
working-directory: apps/desktop working-directory: apps/desktop
run: pnpm run fetch --noverify -d ${CONFIG} ${VERSION} run: pnpm run fetch --noverify -d ${CONFIG} ${VERSION}
env: env:
@@ -189,7 +186,7 @@ jobs:
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
with: with:
name: webapp name: desktop-prepare
retention-days: 1 retention-days: 1
path: | path: |
apps/desktop/webapp.asar apps/desktop/webapp.asar
+13 -5
View File
@@ -48,8 +48,7 @@ jobs:
cache: "pnpm" cache: "pnpm"
- name: Install Deps - name: Install Deps
working-directory: apps/desktop run: "pnpm install --frozen-lockfile --filter element-desktop"
run: "pnpm install --frozen-lockfile"
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with: with:
@@ -85,12 +84,19 @@ jobs:
EXECUTABLE: ${{ steps.executable.outputs.path }} EXECUTABLE: ${{ steps.executable.outputs.path }}
- name: Run tests - name: Run tests
uses: coactions/setup-xvfb@6b00cf1889f4e1d5a48635647013c0508128ee1a
timeout-minutes: 20 timeout-minutes: 20
with: shell: bash
run: pnpm -C apps/desktop test --project=${{ inputs.project }} ${{ runner.os != 'Linux' && '--ignore-snapshots' || '' }} ${{ inputs.blob_report == false && '--reporter=html' || '' }} ${{ inputs.args }} working-directory: apps/desktop
run: |
$PREFIX pnpm playwright test \
${{ runner.os != 'Linux' && '--ignore-snapshots' || '' }} \
${{ inputs.blob_report == false && '--reporter=html' || '' }} \
$ARGS
env: env:
PREFIX: ${{ runner.os == 'Linux' && 'xvfb-run' || '' }}
PW_TAG: ${{ inputs.project }}
ELEMENT_DESKTOP_EXECUTABLE: ${{ steps.executable.outputs.path }} ELEMENT_DESKTOP_EXECUTABLE: ${{ steps.executable.outputs.path }}
ARGS: ${{ inputs.args }}
- name: Upload blob report - name: Upload blob report
if: always() && inputs.blob_report if: always() && inputs.blob_report
@@ -99,6 +105,7 @@ jobs:
name: blob-report-${{ inputs.artifact }} name: blob-report-${{ inputs.artifact }}
path: apps/desktop/blob-report path: apps/desktop/blob-report
retention-days: 1 retention-days: 1
if-no-files-found: error
- name: Upload HTML report - name: Upload HTML report
if: always() && inputs.blob_report == false if: always() && inputs.blob_report == false
@@ -107,3 +114,4 @@ jobs:
name: ${{ inputs.artifact }}-test name: ${{ inputs.artifact }}-test
path: apps/desktop/playwright-report path: apps/desktop/playwright-report
retention-days: 14 retention-days: 14
if-no-files-found: error
+4 -4
View File
@@ -42,7 +42,7 @@ on:
type: string type: string
required: false required: false
description: | description: |
The name of the prepare artifact to use, defaults to 'webapp'. The name of the prepare artifact to use, defaults to 'desktop-prepare'.
The artifact must contain the following: The artifact must contain the following:
+ webapp.asar - the asar archive of the webapp to embed in the desktop app + webapp.asar - the asar archive of the webapp to embed in the desktop app
+ electronVersion - the version of electron to use for cache keying + electronVersion - the version of electron to use for cache keying
@@ -52,7 +52,7 @@ on:
The artifact can also contain any additional files which will be applied as overrides to the checkout root before building, The artifact can also contain any additional files which will be applied as overrides to the checkout root before building,
for example icons in the `build/` directory to override the app icons. for example icons in the `build/` directory to override the app icons.
default: "webapp" default: "desktop-prepare"
test: test:
type: boolean type: boolean
required: false required: false
@@ -123,7 +123,7 @@ jobs:
id: cache id: cache
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5 uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with: with:
key: ${{ runner.os }}-${{ inputs.arch }}-${{ hashFiles('hakHash', 'electronVersion') }} key: ${{ runner.os }}-${{ inputs.arch }}-${{ hashFiles('apps/desktop/hakHash', 'apps/desktop/electronVersion') }}
path: | path: |
apps/desktop/.hak apps/desktop/.hak
@@ -160,7 +160,7 @@ jobs:
- name: Install Deps - name: Install Deps
working-directory: apps/desktop working-directory: apps/desktop
run: "pnpm install --frozen-lockfile" run: "pnpm install --frozen-lockfile --filter element-desktop"
- name: Insert config snippet - name: Insert config snippet
if: steps.config.outputs.extra_config != '' if: steps.config.outputs.extra_config != ''
+9 -4
View File
@@ -14,10 +14,7 @@ import type { Tool } from "../../scripts/hak/hakEnv.ts";
import type { DependencyInfo } from "../../scripts/hak/dep.ts"; import type { DependencyInfo } from "../../scripts/hak/dep.ts";
export default async function (hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> { export default async function (hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
const tools: Tool[] = [ const tools: Tool[] = [["rustc", "--version"]];
["rustc", "--version"],
["python", "--version"], // node-gyp uses python for reasons beyond comprehension
];
if (hakEnv.isWin()) { if (hakEnv.isWin()) {
tools.push(["perl", "--version"]); // for openssl configure tools.push(["perl", "--version"]); // for openssl configure
tools.push(["nasm", "-v"]); // for openssl building tools.push(["nasm", "-v"]); // for openssl building
@@ -28,6 +25,14 @@ export default async function (hakEnv: HakEnv, moduleInfo: DependencyInfo): Prom
} }
await hakEnv.checkTools(tools); await hakEnv.checkTools(tools);
try {
// node-gyp uses python for reasons beyond comprehension
await hakEnv.checkTools([["python", "--version"]]);
} catch {
// try python3 too
await hakEnv.checkTools([["python3", "--version"]]);
}
// Ensure Rust target exists (nb. we avoid depending on rustup) // Ensure Rust target exists (nb. we avoid depending on rustup)
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
const rustc = childProcess.execFile( const rustc = childProcess.execFile(
+2 -18
View File
@@ -8,25 +8,9 @@ Please see LICENSE files in the repository root for full details.
import { defineConfig } from "@playwright/test"; import { defineConfig } from "@playwright/test";
const projects = [
"macos",
"win-x64",
"win-ia32",
"win-arm64",
"linux-amd64-sqlcipher-system",
"linux-amd64-sqlcipher-static",
"linux-arm64-sqlcipher-system",
"linux-arm64-sqlcipher-static",
];
export default defineConfig({ export default defineConfig({
// Allows the GitHub action to specify a project name (OS + arch) for the combined report to make sense projects: [{ name: "Desktop" }],
// workaround for https://github.com/microsoft/playwright/issues/33521 tag: process.env.PW_TAG ? `@${process.env.PW_TAG}` : undefined,
projects: process.env.CI
? projects.map((name) => ({
name,
}))
: undefined,
use: { use: {
viewport: { width: 1280, height: 720 }, viewport: { width: 1280, height: 720 },
video: "retain-on-failure", video: "retain-on-failure",
+2 -1
View File
@@ -1,6 +1,7 @@
{ {
"$schema": "../../node_modules/nx/schemas/project-schema.json", "$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "app", "projectType": "application",
"implicitDependencies": ["element-web"],
"root": "apps/desktop", "root": "apps/desktop",
"targets": { "targets": {
"docker:build": { "docker:build": {
-48
View File
@@ -1,48 +0,0 @@
#!/bin/bash
# Script for downloading a branch of element-web matching the branch a PR is contributed from
set -x
deforg="element-hq"
defrepo="element-web"
# The PR_NUMBER variable must be set explicitly.
default_org_repo=${GITHUB_REPOSITORY:-"$deforg/$defrepo"}
PR_ORG=${PR_ORG:-${default_org_repo%%/*}}
PR_REPO=${PR_REPO:-${default_org_repo##*/}}
# A function that clones a branch of a repo based on the org, repo and branch
clone() {
org=$1
repo=$2
branch=$3
if [ -n "$branch" ]
then
echo "Trying to use $org/$repo#$branch"
# Disable auth prompts: https://serverfault.com/a/665959
GIT_TERMINAL_PROMPT=0 git clone https://github.com/$org/$repo.git $repo --branch "$branch" --depth 1 && exit 0
fi
}
echo "Getting info about a PR with number $PR_NUMBER"
apiEndpoint="https://api.github.com/repos/$PR_ORG/$PR_REPO/pulls/$PR_NUMBER"
head=$(curl "$apiEndpoint" | jq -r '.head.label')
# for forks, $head will be in the format "fork:branch", so we split it by ":"
# into an array. On non-forks, this has the effect of splitting into a single
# element array given ":" shouldn't appear in the head - it'll just be the
# branch name. Based on the results, we clone.
BRANCH_ARRAY=(${head//:/ })
TRY_ORG=$deforg
TRY_BRANCH=${BRANCH_ARRAY[0]}
if [[ "$head" == *":"* ]]; then
# ... but only match that fork if it's a real fork
if [ "${BRANCH_ARRAY[0]}" != "$PR_ORG" ]; then
TRY_ORG=${BRANCH_ARRAY[0]}
fi
TRY_BRANCH=${BRANCH_ARRAY[1]}
fi
clone "$TRY_ORG" "$defrepo" "$TRY_BRANCH"
exit 1
+3
View File
@@ -75,5 +75,8 @@ export default {
nx: { nx: {
config: ["{nx,package,project}.json", "{apps,packages,modules}/**/{package,project}.json"], config: ["{nx,package,project}.json", "{apps,packages,modules}/**/{package,project}.json"],
}, },
playwright: {
config: ["playwright.config.ts", "playwright-merge.config.ts"],
},
tags: ["-knipignore"], tags: ["-knipignore"],
} satisfies KnipConfig; } satisfies KnipConfig;
+2
View File
@@ -27,9 +27,11 @@
"devDependencies": { "devDependencies": {
"@action-validator/cli": "^0.6.0", "@action-validator/cli": "^0.6.0",
"@action-validator/core": "^0.6.0", "@action-validator/core": "^0.6.0",
"@element-hq/element-web-playwright-common": "catalog:",
"@nx-tools/nx-container": "^7.2.1", "@nx-tools/nx-container": "^7.2.1",
"@nx/jest": "^22.5.0", "@nx/jest": "^22.5.0",
"@types/node": "22", "@types/node": "22",
"@playwright/test": "catalog:",
"cronstrue": "^3.0.0", "cronstrue": "^3.0.0",
"eslint-plugin-matrix-org": "^3.0.0", "eslint-plugin-matrix-org": "^3.0.0",
"husky": "^9.0.0", "husky": "^9.0.0",
@@ -26,7 +26,7 @@ type PaginationLinks = {
// We see quite a few test flakes which are caused by the app exploding // We see quite a few test flakes which are caused by the app exploding
// so we have some magic strings we check the logs for to better track the flake with its cause // so we have some magic strings we check the logs for to better track the flake with its cause
const SPECIAL_CASES = { const SPECIAL_CASES: Record<string, string> = {
"ChunkLoadError": "ChunkLoadError", "ChunkLoadError": "ChunkLoadError",
"Unreachable code should not be executed": "Rust crypto panic", "Unreachable code should not be executed": "Rust crypto panic",
"Out of bounds memory access": "Rust crypto memory error", "Out of bounds memory access": "Rust crypto memory error",
@@ -37,7 +37,7 @@ class FlakyReporter implements Reporter {
public onTestEnd(test: TestCase): void { public onTestEnd(test: TestCase): void {
// Ignores flakes on Dendrite and Pinecone as they have their own flakes we do not track // Ignores flakes on Dendrite and Pinecone as they have their own flakes we do not track
if (["Dendrite", "Pinecone"].includes(test.parent.project()?.name)) return; if (["Dendrite", "Pinecone"].includes(test.parent.project()!.name!)) return;
let failures = [`${test.location.file.split("playwright/e2e/")[1]}: ${test.title}`]; let failures = [`${test.location.file.split("playwright/e2e/")[1]}: ${test.title}`];
if (test.outcome() === "flaky") { if (test.outcome() === "flaky") {
const timedOutRuns = test.results.filter((result) => result.status === "timedOut"); const timedOutRuns = test.results.filter((result) => result.status === "timedOut");
@@ -46,7 +46,7 @@ class FlakyReporter implements Reporter {
); );
// If a test failed due to a systemic fault then the test is not flaky, the app is, record it as such. // If a test failed due to a systemic fault then the test is not flaky, the app is, record it as such.
const specialCases = Object.keys(SPECIAL_CASES).filter((log) => const specialCases = Object.keys(SPECIAL_CASES).filter((log) =>
pageLogs.some((attachment) => attachment.name.startsWith("page-") && attachment.body.includes(log)), pageLogs.some((attachment) => attachment.name.startsWith("page-") && attachment.body?.includes(log)),
); );
if (specialCases.length > 0) { if (specialCases.length > 0) {
failures = specialCases.map((specialCase) => SPECIAL_CASES[specialCase]); failures = specialCases.map((specialCase) => SPECIAL_CASES[specialCase]);
@@ -56,7 +56,7 @@ class FlakyReporter implements Reporter {
if (!this.flakes.has(title)) { if (!this.flakes.has(title)) {
this.flakes.set(title, []); this.flakes.set(title, []);
} }
this.flakes.get(title).push(test); this.flakes.get(title)!.push(test);
} }
} }
} }
@@ -76,8 +76,8 @@ class FlakyReporter implements Reporter {
if (!link) return map; if (!link) return map;
const matches = link.matchAll(/(<(?<link>.+?)>; rel="(?<type>.+?)")/g); const matches = link.matchAll(/(<(?<link>.+?)>; rel="(?<type>.+?)")/g);
for (const match of matches) { for (const match of matches) {
const { link, type } = match.groups; const { link, type } = match.groups!;
map[type] = link; map[type as keyof PaginationLinks] = link;
} }
return map; return map;
} }
@@ -102,9 +102,9 @@ class FlakyReporter implements Reporter {
issues.push(...fetchedIssues); issues.push(...fetchedIssues);
// Get the next link for fetching more results // Get the next link for fetching more results
const linkHeader = issuesResponse.headers.get("Link"); const linkHeader = issuesResponse.headers.get("Link")!;
const parsed = this.parseLinkHeader(linkHeader); const parsed = this.parseLinkHeader(linkHeader);
url = parsed.next; url = parsed.next!;
} }
return issues; return issues;
} }
+20
View File
@@ -0,0 +1,20 @@
/*
Copyright 2026 Element Creations Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { defineConfig } from "@playwright/test";
export default defineConfig({
// Playwright only supports merging using a single testDir, specifying web here
// means that some parts of the report for Desktop will be incorrect, but this is minor.
// https://github.com/microsoft/playwright/issues/39855
testDir: "apps/web/playwright/e2e",
reporter: [
["html", { open: "never" }],
["./packages/playwright-common/flaky-reporter.ts"],
["@element-hq/element-web-playwright-common/lib/stale-screenshot-reporter.js"],
],
});
+23 -10
View File
@@ -135,12 +135,18 @@ importers:
'@action-validator/core': '@action-validator/core':
specifier: ^0.6.0 specifier: ^0.6.0
version: 0.6.0 version: 0.6.0
'@element-hq/element-web-playwright-common':
specifier: 'catalog:'
version: 2.2.7(@element-hq/element-web-module-api@1.12.0(@matrix-org/react-sdk-module-api@2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4))(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(matrix-web-i18n@3.6.0)(react@19.2.4))(@playwright/test@1.58.2)(playwright-core@1.58.2)
'@nx-tools/nx-container': '@nx-tools/nx-container':
specifier: ^7.2.1 specifier: ^7.2.1
version: 7.2.1(@nx/devkit@22.5.3(nx@22.5.4))(@nx/js@22.5.3(@babel/traverse@7.29.0)(nx@22.5.4))(dotenv@17.3.1)(nx@22.5.4)(tslib@2.8.1) version: 7.2.1(@nx/devkit@22.5.3(nx@22.5.4))(@nx/js@22.5.3(@babel/traverse@7.29.0)(nx@22.5.4))(dotenv@17.3.1)(nx@22.5.4)(tslib@2.8.1)
'@nx/jest': '@nx/jest':
specifier: ^22.5.0 specifier: ^22.5.0
version: 22.5.3(@babel/traverse@7.29.0)(@types/node@18.19.130)(babel-plugin-macros@3.1.0)(nx@22.5.4)(typescript@5.9.3) version: 22.5.3(@babel/traverse@7.29.0)(@types/node@18.19.130)(babel-plugin-macros@3.1.0)(nx@22.5.4)(typescript@5.9.3)
'@playwright/test':
specifier: 'catalog:'
version: 1.58.2
'@types/node': '@types/node':
specifier: 18.19.130 specifier: 18.19.130
version: 18.19.130 version: 18.19.130
@@ -179,10 +185,10 @@ importers:
version: 5.9.3 version: 5.9.3
vitepress: vitepress:
specifier: ^1.6.4 specifier: ^1.6.4
version: 1.6.4(@algolia/client-search@5.50.0)(@types/node@18.19.130)(@types/react@19.2.10)(axios@1.13.5)(jwt-decode@4.0.0)(lightningcss@1.32.0)(postcss@8.5.8)(qrcode@1.5.4)(search-insights@2.17.3)(sugarss@5.0.1(postcss@8.5.8))(terser@5.46.0)(typescript@5.9.3) version: 1.6.4(@algolia/client-search@5.50.0)(@types/node@18.19.130)(@types/react@19.2.10)(axios@1.13.5)(jwt-decode@4.0.0)(lightningcss@1.32.0)(postcss@8.5.8)(qrcode@1.5.4)(react@19.2.4)(search-insights@2.17.3)(sugarss@5.0.1(postcss@8.5.8))(terser@5.46.0)(typescript@5.9.3)
vitepress-plugin-mermaid: vitepress-plugin-mermaid:
specifier: ^2.0.17 specifier: ^2.0.17
version: 2.0.17(mermaid@11.13.0)(vitepress@1.6.4(@algolia/client-search@5.50.0)(@types/node@18.19.130)(@types/react@19.2.10)(axios@1.13.5)(jwt-decode@4.0.0)(lightningcss@1.32.0)(postcss@8.5.8)(qrcode@1.5.4)(search-insights@2.17.3)(sugarss@5.0.1(postcss@8.5.8))(terser@5.46.0)(typescript@5.9.3)) version: 2.0.17(mermaid@11.13.0)(vitepress@1.6.4(@algolia/client-search@5.50.0)(@types/node@18.19.130)(@types/react@19.2.10)(axios@1.13.5)(jwt-decode@4.0.0)(lightningcss@1.32.0)(postcss@8.5.8)(qrcode@1.5.4)(react@19.2.4)(search-insights@2.17.3)(sugarss@5.0.1(postcss@8.5.8))(terser@5.46.0)(typescript@5.9.3))
yaml: yaml:
specifier: 2.8.3 specifier: 2.8.3
version: 2.8.3 version: 2.8.3
@@ -11457,6 +11463,10 @@ packages:
sanitize-html@2.17.1: sanitize-html@2.17.1:
resolution: {integrity: sha512-ehFCW+q1a4CSOWRAdX97BX/6/PDEkCqw7/0JXZAGQV57FQB3YOkTa/rrzHPeJ+Aghy4vZAFfWMYyfxIiB7F/gw==} resolution: {integrity: sha512-ehFCW+q1a4CSOWRAdX97BX/6/PDEkCqw7/0JXZAGQV57FQB3YOkTa/rrzHPeJ+Aghy4vZAFfWMYyfxIiB7F/gw==}
sax@1.4.4:
resolution: {integrity: sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==}
engines: {node: '>=11.0.0'}
sax@1.6.0: sax@1.6.0:
resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==}
engines: {node: '>=11.0.0'} engines: {node: '>=11.0.0'}
@@ -14541,9 +14551,9 @@ snapshots:
'@docsearch/css@3.8.2': {} '@docsearch/css@3.8.2': {}
'@docsearch/js@3.8.2(@algolia/client-search@5.50.0)(@types/react@19.2.10)(search-insights@2.17.3)': '@docsearch/js@3.8.2(@algolia/client-search@5.50.0)(@types/react@19.2.10)(react@19.2.4)(search-insights@2.17.3)':
dependencies: dependencies:
'@docsearch/react': 3.8.2(@algolia/client-search@5.50.0)(@types/react@19.2.10)(search-insights@2.17.3) '@docsearch/react': 3.8.2(@algolia/client-search@5.50.0)(@types/react@19.2.10)(react@19.2.4)(search-insights@2.17.3)
preact: 10.28.3 preact: 10.28.3
transitivePeerDependencies: transitivePeerDependencies:
- '@algolia/client-search' - '@algolia/client-search'
@@ -14552,7 +14562,7 @@ snapshots:
- react-dom - react-dom
- search-insights - search-insights
'@docsearch/react@3.8.2(@algolia/client-search@5.50.0)(@types/react@19.2.10)(search-insights@2.17.3)': '@docsearch/react@3.8.2(@algolia/client-search@5.50.0)(@types/react@19.2.10)(react@19.2.4)(search-insights@2.17.3)':
dependencies: dependencies:
'@algolia/autocomplete-core': 1.17.7(@algolia/client-search@5.50.0)(algoliasearch@5.50.0)(search-insights@2.17.3) '@algolia/autocomplete-core': 1.17.7(@algolia/client-search@5.50.0)(algoliasearch@5.50.0)(search-insights@2.17.3)
'@algolia/autocomplete-preset-algolia': 1.17.7(@algolia/client-search@5.50.0)(algoliasearch@5.50.0) '@algolia/autocomplete-preset-algolia': 1.17.7(@algolia/client-search@5.50.0)(algoliasearch@5.50.0)
@@ -14560,6 +14570,7 @@ snapshots:
algoliasearch: 5.50.0 algoliasearch: 5.50.0
optionalDependencies: optionalDependencies:
'@types/react': 19.2.10 '@types/react': 19.2.10
react: 19.2.4
search-insights: 2.17.3 search-insights: 2.17.3
transitivePeerDependencies: transitivePeerDependencies:
- '@algolia/client-search' - '@algolia/client-search'
@@ -19317,7 +19328,7 @@ snapshots:
builder-util-runtime@9.5.1: builder-util-runtime@9.5.1:
dependencies: dependencies:
debug: 4.4.3 debug: 4.4.3
sax: 1.6.0 sax: 1.4.4
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -25261,6 +25272,8 @@ snapshots:
parse-srcset: 1.0.2 parse-srcset: 1.0.2
postcss: 8.5.8 postcss: 8.5.8
sax@1.4.4: {}
sax@1.6.0: {} sax@1.6.0: {}
saxes@6.0.0: saxes@6.0.0:
@@ -26687,17 +26700,17 @@ snapshots:
tsx: 4.21.0 tsx: 4.21.0
yaml: 2.8.3 yaml: 2.8.3
vitepress-plugin-mermaid@2.0.17(mermaid@11.13.0)(vitepress@1.6.4(@algolia/client-search@5.50.0)(@types/node@18.19.130)(@types/react@19.2.10)(axios@1.13.5)(jwt-decode@4.0.0)(lightningcss@1.32.0)(postcss@8.5.8)(qrcode@1.5.4)(search-insights@2.17.3)(sugarss@5.0.1(postcss@8.5.8))(terser@5.46.0)(typescript@5.9.3)): vitepress-plugin-mermaid@2.0.17(mermaid@11.13.0)(vitepress@1.6.4(@algolia/client-search@5.50.0)(@types/node@18.19.130)(@types/react@19.2.10)(axios@1.13.5)(jwt-decode@4.0.0)(lightningcss@1.32.0)(postcss@8.5.8)(qrcode@1.5.4)(react@19.2.4)(search-insights@2.17.3)(sugarss@5.0.1(postcss@8.5.8))(terser@5.46.0)(typescript@5.9.3)):
dependencies: dependencies:
mermaid: 11.13.0 mermaid: 11.13.0
vitepress: 1.6.4(@algolia/client-search@5.50.0)(@types/node@18.19.130)(@types/react@19.2.10)(axios@1.13.5)(jwt-decode@4.0.0)(lightningcss@1.32.0)(postcss@8.5.8)(qrcode@1.5.4)(search-insights@2.17.3)(sugarss@5.0.1(postcss@8.5.8))(terser@5.46.0)(typescript@5.9.3) vitepress: 1.6.4(@algolia/client-search@5.50.0)(@types/node@18.19.130)(@types/react@19.2.10)(axios@1.13.5)(jwt-decode@4.0.0)(lightningcss@1.32.0)(postcss@8.5.8)(qrcode@1.5.4)(react@19.2.4)(search-insights@2.17.3)(sugarss@5.0.1(postcss@8.5.8))(terser@5.46.0)(typescript@5.9.3)
optionalDependencies: optionalDependencies:
'@mermaid-js/mermaid-mindmap': 9.3.0 '@mermaid-js/mermaid-mindmap': 9.3.0
vitepress@1.6.4(@algolia/client-search@5.50.0)(@types/node@18.19.130)(@types/react@19.2.10)(axios@1.13.5)(jwt-decode@4.0.0)(lightningcss@1.32.0)(postcss@8.5.8)(qrcode@1.5.4)(search-insights@2.17.3)(sugarss@5.0.1(postcss@8.5.8))(terser@5.46.0)(typescript@5.9.3): vitepress@1.6.4(@algolia/client-search@5.50.0)(@types/node@18.19.130)(@types/react@19.2.10)(axios@1.13.5)(jwt-decode@4.0.0)(lightningcss@1.32.0)(postcss@8.5.8)(qrcode@1.5.4)(react@19.2.4)(search-insights@2.17.3)(sugarss@5.0.1(postcss@8.5.8))(terser@5.46.0)(typescript@5.9.3):
dependencies: dependencies:
'@docsearch/css': 3.8.2 '@docsearch/css': 3.8.2
'@docsearch/js': 3.8.2(@algolia/client-search@5.50.0)(@types/react@19.2.10)(search-insights@2.17.3) '@docsearch/js': 3.8.2(@algolia/client-search@5.50.0)(@types/react@19.2.10)(react@19.2.4)(search-insights@2.17.3)
'@iconify-json/simple-icons': 1.2.75 '@iconify-json/simple-icons': 1.2.75
'@shikijs/core': 2.5.0 '@shikijs/core': 2.5.0
'@shikijs/transformers': 2.5.0 '@shikijs/transformers': 2.5.0
+3 -1
View File
@@ -18,11 +18,13 @@ sonar.coverage.exclusions=\
apps/web/scripts/**/*,\ apps/web/scripts/**/*,\
apps/desktop/scripts/**/*,\ apps/desktop/scripts/**/*,\
apps/desktop/playwright/**/*,\ apps/desktop/playwright/**/*,\
apps/desktop/hak/**/*,\
scripts/**/*,\ scripts/**/*,\
apps/web/src/components/views/dialogs/devtools/**/*,\ apps/web/src/components/views/dialogs/devtools/**/*,\
apps/web/src/utils/SessionLock.ts,\ apps/web/src/utils/SessionLock.ts,\
apps/web/src/**/*.d.ts,\ apps/web/src/**/*.d.ts,\
apps/web/src/vector/mobile_guide/**/*,\ apps/web/src/vector/mobile_guide/**/*,\
packages/shared-components/src/test/**/*,\ packages/shared-components/src/test/**/*,\
packages/shared-components/src/**/*.stories.tsx packages/shared-components/src/**/*.stories.tsx,\
packages/playwright-common/**/*
sonar.testExecutionReportPaths=apps/web/coverage/jest-sonar-report.xml sonar.testExecutionReportPaths=apps/web/coverage/jest-sonar-report.xml