* perf: load chat and community sections asynchronously with skeletons The section loaders own the chrome (StatusSectionLayout) and show the matching skeleton — with the real, functional header — while the actual section incubates behind an asynchronous Loader; the section's panels are proxied back into the loader-owned chrome once ready (full-page community views hide it). The communities portal loader turns asynchronous too. The pre-ready startup overlay recomposes from the skeleton panels, replacing the ChatLayoutLoading placeholder view (whose AppMain reference had been left dangling). * fixup! perf: load chat and community sections asynchronously with skeletons Each skeleton slot was gated on the whole section loader reaching Ready, so the header, chat list, message column and member list all swapped in at once, paced by the slowest panel. ChatView now publishes per-panel readiness and ChatLayout forwards it, letting each slot retire on its own. `root.item` is null until the section itself loads, so the old whole-loader behaviour remains the floor. The right panel is not deferred at this point in the stack, so its flag is constant; it becomes meaningful once the members panel moves behind a loader. Also drops `signal ready()` from both section loaders: `mainReady` is fed to AppMain by AppMainLoader and nothing has ever connected to `ready`, so the signal and the comment claiming AppMain owns the spinner toggle are both dead. * fixup! perf: load chat and community sections asynchronously with skeletons The startup overlay predates the loader-owned chrome. ChatLoader and CommunityChatLoader declare their skeleton panels as plain children, which Qt instantiates and paints even while the loader is inactive, so for the chat section the overlay now covers an already-correct skeleton with a duplicate of itself. Scoped to the two cases where nothing else paints yet: the communities portal (CommunitiesPortalLoader is gated on mainReady and owns no skeleton), and a community section during the window where activeSectionType is already `community` but the repeater has not produced any delegate — the same window the currentIndex fallback below already accounts for. * fixup! perf: load chat and community sections asynchronously with skeletons Release the chrome skeletons instead of hiding them: permanent visible:-gated skeletons re-evaluate their tile geometry bindings on every resize for the lifetime of the section (same class as the ChatContentView messages skeleton, caught in a resize profile). Each skeleton now lives behind a Loader gated on its slot's readiness. * fixup! perf: load chat and community sections asynchronously with skeletons The slots fell through to the real panel as soon as `root.item` existed. ChatView's panels are non-null Loader objects from the moment the section loads, so `?? skeleton` never saw the readiness flags: the LayoutItemProxy retargeted to an empty Loader, released the skeleton with setParentItem(null)/setVisible(false), and the slot painted nothing until the panel incubated. Gate each slot on its own flag instead. The skeletons collapse to bare Loaders keyed on `!ready`, dropping the wrapper Item whose `visible` the proxy also writes. tst_SectionLoaderChrome drives both loaders with a stub section whose flags are writable, so the contract is pinned as a binding rather than as a race against incubation. * fixup! perf: load chat and community sections asynchronously with skeletons The startup overlay's only remaining case was a community section with no repeater delegate yet, and it painted the chat-list skeleton there — wrong shape, replaced a frame later by CommunityChannelsSkeleton. Keep the overlay for the communities portal, which has nothing else painting, and let a missing community delegate fall back to the currentIndex loader, which already shows the right shape. * fixup! perf: load chat and community sections asynchronously with skeletons * chore(@e2e): fix tests according to the changes * fixup! perf: load chat and community sections asynchronously with skeletons * fixup! perf: load chat and community sections asynchronously with skeletons --------- Co-authored-by: Anastasiya <anastasija.ig@gmail.com>
Desktop UI tests (e2e)
Run automated UI tests for Status desktop on Linux, Windows, or macOS.
Pick your platform below and follow the steps in order.
Before you begin
- Clone the repo and open a terminal in
test/e2e. - Get a Status app — CI build or local dev build (see Which app to use).
- Install Squish — required on all platforms (Qt Squish).
Linux
Details: Notion — Linux setup.
One-time setup
cd test/e2e
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp configs/_local.default.py configs/_local.py
Edit configs/_local.py — set AUT_PATH:
AUT_PATH = "/path/to/Status.AppImage"
# or local dev build:
# AUT_PATH = "/path/to/status-app/bin/nim_status_client"
Set SQUISH_DIR (e.g. /opt/squish-runner-9.2.2-qt-6.11).
Run tests
cd test/e2e
source .venv/bin/activate
pytest -m critical
Windows
Details: Notion — Windows setup.
One-time setup
cd test\e2e
python -m venv .venv
.venv\Scripts\activate
pip install -r requirements.txt
copy configs\_local.default.py configs\_local.py
Edit configs/_local.py — set AUT_PATH:
AUT_PATH = "C:\\Users\\you\\AppData\\Local\\StatusApp\\bin\\Status.exe"
# or local dev build:
# AUT_PATH = "C:\\path\\to\\status-app\\bin\\Status.exe"
Set SQUISH_DIR (e.g. C:\squish-runner-9.2.2-qt-6.11).
Run tests
cd test\e2e
.venv\Scripts\activate
pytest -m critical
macOS
Apple Silicon (M-series) needs a few extra pieces beyond Linux/Windows. Squish 9.2.2’s Python and squishserver are x86_64 (Rosetta). A clean Mac has no Intel / python.org Python at /Library/Frameworks/Python.framework — do not install one. The setup script uses Squish’s bundled Python (python3.10 with @loader_path). Do not create the venv from Homebrew, python.org, or Squish’s python3.10-intel64 (that stub looks for the missing system framework and crashes).
macOS — one-time setup
Do these in order on a clean Mac.
- Rosetta 2 (required):
softwareupdate --install-rosetta --agree-to-license
- Xcode.app from the Mac App Store (not only Command Line Tools). Squish IDE / Inspector fail with “Xcode installation was not found” without it. Open Xcode once, then:
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
sudo xcodebuild -license accept
-
Install Squish 9.2.2 into
/Applications/Squish_9_2_2and activate the license (Qt Squish). -
Install the same Qt patch Status and Squish were built with (a mismatch shows
StatusDialog unavailable/QtPrivate_6_11_0in the AUT log). Point Squish at that kit’sQtCore.framework(path depends on how you installed Qt):
/Applications/Squish_9_2_2/bin/squishconfig --qt=/path/to/QtCore.framework
-
Get Status.app — CI DMG with Squish entitlements (see Mac CI build).
-
x86_64 OpenSSL (Intel Homebrew — not
/opt/homebrew). Needed to compilescrypt:
# Intel Homebrew once, if /usr/local/bin/brew is missing:
# arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
arch -x86_64 /usr/local/bin/brew install openssl@3
Always call Intel brew as arch -x86_64 /usr/local/bin/brew.
- From
test/e2e:
cd test/e2e
./scripts/setup_mac_squish.sh
The script creates .venv from Squish’s bundled Python (no system Intel Python needed), installs requirements.txt, and copies configs/_local.py / .env if they are missing. Override Squish path with SQUISH_DIR=... if needed. Do not put SQUISH_DIR or PYTHONPATH in ~/.zshrc.
- Edit
configs/_local.py:
AUT_PATH = "/Users/you/Downloads/Status.app"
# or local dev build:
# AUT_PATH = "/Users/you/status-app/bin/nim_status_client"
Run tests (Terminal)
cd test/e2e
source .venv/bin/activate
pytest tests/onboarding/test_language_selector_and_password_strength.py -v --maxfail=1
# or the PR suite:
# pytest -m critical
Run tests (PyCharm)
- Complete macOS one-time setup above.
- Interpreter:
test/e2e/.venv/bin/python— select existing, do not create a new venv from Homebrew or python.org. - Working directory:
test/e2e - Use
.env(SQUISH_DIR/PYTHONPATH) — the setup script copies.env.example→.envif needed. - Set
AUT_PATHinconfigs/_local.py. - Run pytest, e.g. the onboarding file above or
-m critical.
Which app to use
| Source | Linux | Windows | macOS |
|---|---|---|---|
| CI nightly | .AppImage from Jenkins nightly |
Status.exe from nightly |
DMG with Squish entitlements (below) |
| Local dev build | bin/nim_status_client or AppImage |
bin\Status.exe |
Status.app or bin/nim_status_client |
CI builds are usually more stable; local dev builds work on all platforms.
Getting a Mac build from CI
macOS e2e runs locally only (Linux and Windows run in Jenkins). You need a DMG built with Squish entitlements:
- https://ci.status.im/job/status-desktop/job/systems/job/macos/
- Entitlements →
resources/Entitlements_squish.plist - Download DMG, copy
Status.app(e.g.~/Downloads/Status.app) AUT_PATH = "/Users/you/Downloads/Status.app"inconfigs/_local.py
Keycard e2e (@pytest.mark.keycard)
Needs a build with simulated keycard (USE_SIMULATED_KEYCARD=true). Set AUT_PATH in configs/_local.py, then:
cd test/e2e && source .venv/bin/activate
pytest -m keycard -v
Requires a JRE ≥ 11 on PATH (packaged builds ship the simulator, but still start it with the host JVM).
1. Dev build — from repo root:
USE_SIMULATED_KEYCARD=true make -j12
AUT_PATH → …/status-app/bin/StatusDev (or the platform equivalent under bin/).
2. Packaged build (Status.app / AppImage / .exe) — CI Build with Parameters → enable USE_SIMULATED_KEYCARD (on macOS also Squish entitlements as above). AUT_PATH → the packaged app.
CI (nightly): the nightly job builds a separate Linux and Windows package with USE_SIMULATED_KEYCARD=true (not published as the nightly artifacts) and runs tests-e2e with KEYCARD_TESTS=true (pytest -m keycard). Manual run: same two parameters — package with USE_SIMULATED_KEYCARD, then e2e with KEYCARD_TESTS and BUILD_SOURCE pointing at that package. Windows e2e agents need a JRE ≥ 11 on PATH.
Logs
All platforms write to test/e2e/local_run_results/:
pytest.log— test runneraut.log— app launch (startaut)squish.log— Squish server
Per-run screenshots and data: local_run_results/run_<date>/.
Local Waku fleet (optional)
Run against a local nwaku stack instead of status.prod. Works on Linux, Windows, and macOS — same env vars and compose file everywhere.
Prerequisites: Docker installed and running (docker ps must work). On Mac/Windows use Docker Desktop and wait until it is fully started before running compose.
From the repo root (status-app/):
docker compose -f ./docker-compose.waku.yml up --build --remove-orphans
Compose does not write the fleets JSON. Generate it from the repo root (--project must match compose; default is e2e-fleet if you passed -p e2e-fleet, otherwise usually status-app):
python3 test/e2e/scripts/scan_waku_fleet.py --project <compose-project>
That writes assets/local-waku-fleets-config.json.
Leave this running. In another terminal, from test/e2e with your venv activated:
export E2E_LOCAL_WAKU_FLEET=1
# export STATUS_FLEET=status-app.test # optional; must match assets/local-waku-fleets-config.json
pytest -m critical
Without E2E_LOCAL_WAKU_FLEET, the app uses built-in fleets (e.g. status.prod). CI sets this on Linux and Windows in ci/Jenkinsfile.tests-e2e.
Pytest marks
pytest -m critical # main PR checks
pytest --markers # list all marks
critical— important desktop PR checkskeycard— simulated keycard tests (USE_SIMULATED_KEYCARD=truebuild)skip— skipped tests (usually with a ticket)timeout(...)— hanging-test guard (pytest-timeout)
Troubleshooting
| Problem | What to try |
|---|---|
SQUISH_DIR error |
Mac: source .venv/bin/activate. Linux/Windows: export SQUISH_DIR=.... PyCharm: use .env. |
| Mac: Squish Python not found | SQUISH_DIR=/Applications/Squish_9_2_2 ./scripts/setup_mac_squish.sh |
Mac: Library not loaded: .../Python.framework/Versions/3.10 / Abort trap: 6 |
Clean Mac has no python.org Intel Python — that is expected. Do not install it and do not use python3.10-intel64 as the venv base. Re-run ./scripts/setup_mac_squish.sh. |
Mac: Failed building wheel for scrypt |
Install x86_64 OpenSSL: arch -x86_64 /usr/local/bin/brew install openssl@3, then re-run setup. ARM Homebrew (/opt/homebrew) will not work. |
Mac: Segmentation fault on import squishtest |
Venv was using python.org’s framework. Re-run ./scripts/setup_mac_squish.sh. |
Mac: Python.framework / squishtest |
Re-run ./scripts/setup_mac_squish.sh |
Mac: EVP_DigestSqueeze |
pip install -r requirements.txt in activated .venv |
| Mac: PyCharm wrong Python | Use existing .venv/bin/python, not Homebrew |
Mac: PyCharm still shows a deleted .venv |
Settings → Python Interpreter → Show All → remove the old interpreter, then add the new .venv/bin/python |
| Mac: Squish IDE “Xcode installation was not found” | Install Xcode.app, then sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer |
Mac: StatusDialog unavailable / QtPrivate_6_11_* in aut.log |
Squish Qt patch ≠ Status.app Qt. Point Squish at the matching kit: squishconfig --qt=/path/to/QtCore.framework. |
App won’t attach / segfault in get_context / attachToApplication |
Use a Status.app built with Squish entitlements. Launch the app once from Finder if Gatekeeper blocks it. |
| Mac: Python quit during test | Retry; check local_run_results/aut.log |
| Test hangs | Try a CI build instead of local dev |
Docker: docker.sock not found |
Start Docker Desktop (Mac/Windows) or the Docker daemon (Linux); verify with docker ps |
unknown fleet with local Waku |
Fleet in saved data must match local-waku-fleets-config.json |
| Windows path errors | Double backslashes in AUT_PATH; use Status.exe in StatusApp folder |
CI overview
| Platform | E2e in Jenkins? | Where to get the app |
|---|---|---|
| Linux | Yes | Nightly + tests-e2e |
| Windows | Yes | Nightly + Windows tests-e2e |
| macOS | No (local only) | macOS systems job |
Jenkins agents set SQUISH_DIR and PYTHONPATH automatically on Linux/Windows.