Most of Sevak is checked automatically on every push. A launcher also depends
on things no test can see: the keyboard shortcut reaching it past the OS, the
permission dialogs of the OS, other people's apps receiving a paste. The
release checklist covers that second kind.
Layer
What it checks
Where
Runs
Static checks
Formatting, clippy with warnings as errors, svelte-check type checking, mkdocs build --strict for broken links
cargo fmt, cargo clippy, npm run check, docs workflow
Every push, on each OS (platform code runs on its own OS)
Rust integration tests
The real search engine and registry with a recording platform: keyword routing, secrets never leaking into global queries, Enter reaching the platform; script plugins and workflows with real child processes
crates/sevak-plugins/tests/
Every push
UI unit tests
Accelerator parsing and identity, settings validation, path helpers, theme colour and contrast maths, theme JSON, workflow graph rules, a few components
ui/src/**/*.test.ts (Vitest)
Every push (UI unit tests job)
Startup integration
Per-user registration, config persistence and rollback, background arguments; Settings toggle through real IPC wrappers with a mocked native bridge
These are why the manual plan exists. None of them can be checked from a test
runner, whatever the coverage number says.
A global hotkey really reaching Sevak instead of the OS or another app
(the Windows keyboard hook, the Spotlight shortcut, GNOME's input-source
shortcut, Wayland's lack of global keys).
Permission dialogs and their wording: macOS Accessibility, Input Monitoring,
Contacts; SmartScreen and Gatekeeper on first run.
Pasting into, and reading a selection from, other people's applications.
The tray or menu-bar icon and its menu on each desktop environment.
What an installer, an upgrade or an uninstall leaves behind.
All Rust tests (the latency test skips itself in a debug build)
cargo test --profile fast-release -p sevak-plugins --test latency -- --nocapture
The latency budget, with timings (the profile is optimized without link-time optimization, so it builds in a few minutes)
cargo bench -p sevak-plugins
The latency benchmark (uses the full release profile, so the first build is slow)
npm ci then npm run check
Install and type-check the UI
npm test
UI unit and component integration tests (Vitest, a few seconds)
npm test -- Settings.startup.test.ts
Settings sign-in toggle, Save/reopen/disable, cancelled draft, OS failure/retry and registration warnings
cargo test -p sevak-platform startup
Startup registration helpers and integration cases without changing the host's real startup entries
npm run test:scripts
Installer source and packaging contracts, including startup controls. On Windows with the Tauri NSIS toolchain, also compiles and executes startup policy and helper-receipt fixtures. Windows bundle/release jobs rerun these after building the installers.
Windows only, after building the installers: inspect the compiled MSI and evaluate its real startup conditions without installing it. CI runs this after the bundle build. Use -MsiPath <file> to select a specific MSI.
No real-time sleeps for correctness. A test that waits 50 ms and hopes is
flaky on a busy runner. Wait for a condition with a generous timeout, or hold
the mock so the order of events is fixed.
No network, and nothing outside a temp directory. Tests use a mock
platform and tempfile; they must not touch the real clipboard, config
folder or browser profiles.
The unit tests of the pure UI logic live next to the code (accelerator.test.ts
beside accelerator.ts). A test that needs a DOM starts with
// @vitest-environment jsdom; Svelte components are tested with
@testing-library/svelte (Toggle.test.ts is a small example). The startup
integration suite renders the full Settings form through its actual IPC
wrappers with a fake Tauri transport: toggling is only a draft until Save,
successful saves reload the persisted choice, and native errors remain visible
without a success toast. These are UI integration tests, not native desktop or
reboot tests.
Coverage is reported, not enforced: there is no threshold. The Coverage
workflow runs on pull requests and weekly, writes the tables below into the job
summary and attaches the lcov and HTML reports as an artifact named coverage.
No outside service is involved.
Reference numbers (Windows host, so the Windows backend of sevak-platform is
what runs; the Linux numbers come from the Coverage workflow and differ for that
crate). Measured with cargo llvm-cov and Vitest.
Crate
Lines
Functions
Regions
sevak-core
97.1% (5585/5749)
95.0% (683/719)
97.0% (10117/10433)
sevak-platform (Windows backend)
79.4% (9819/12369)
76.9% (1299/1690)
80.7% (16791/20796)
sevak-plugins
95.6% (25626/26818)
92.8% (2847/3069)
95.6% (45304/47378)
total
91.3% (41030/44936)
88.2% (4829/5478)
91.9% (72212/78607)
These Rust numbers are generous: the unit tests live in #[cfg(test)] modules inside
the source files, and llvm-cov counts those lines as code that ran. Read them as an
upper bound and look at the HTML report for the uncovered functions, which is where the
real gaps are (OS calls such as the Windows keyboard hook, and anything that needs a
display). The Tauri shell (src-tauri) is not measured: it needs a desktop and the
GTK libraries to build, and its logic is thin glue.
The UI percentages for the whole ui/src tree are low (15% of lines) because Svelte
components are not unit-tested; the logic modules that hold the rules are covered:
The engine logs a warning for any query slower than 16 ms
(sevak_core::engine::LATENCY_BUDGET), the time that still feels instant. Two
things watch it:
engine_query_latency_budget (crates/sevak-plugins/tests/latency.rs)
runs 13 typical queries 40 times each after a warm-up and fails if any
query's 95th percentile is 100 ms or more. The bound is deliberately six
times the target: it is there to catch an accidental O(n²) or a disk read
on the typing path, not to police a few milliseconds on a shared CI runner. A
query gets three rounds and passes with the first round in budget, so one
stalled round does not fail the build; a real regression is slow in every
round. It needs an optimized build and skips itself under debug_assertions.
cargo bench -p sevak-plugins measures the same queries with criterion
(mean, median and confidence interval) plus the startup index build. The
Benchmark workflow runs it weekly on Linux and prints the table in the job
summary; it never fails on a number.
Both build the same fixture (crates/sevak-plugins/benches/fixture/mod.rs):
the real default plugins through the real registry over a mock platform, with
5,000 applications, 100,000 files (the files plugin's own cap), 10,000 bookmarks,
a 200-entry clipboard history and the two example workflows. Nothing reads the
real clipboard, browsers or OS file index. The queries:
Name
Input
What it exercises
single letter
a
Many plugins, nothing worth scoring
short prefix
fir
Fuzzy match over apps, files, bookmarks
app name
visual studio
Two-word match against 5,000 apps
multi-word
budget report meeting
Three words over 100,000 file names
no match anywhere
qzxvkj
Worst case: every index scanned, nothing found
keyword: web / bookmarks / clipboard / files / workflow
g rust lifetimes, b budget, cb copied, f invoice, ddg rust
Keyword routing to one plugin
path browse
~/Documents/rep
A real directory listing of 300 entries
calculator
(12 + 8) * 3 / 4
The calculator, plus every other global plugin
unit conversion
12 km in miles
Units, plus every other global plugin
Reference numbers. Mean time per SearchEngine::query from cargo bench
(release profile, Rust 1.99.0) on the maintainer's development machine: Windows 11,
Intel Core Ultra 9 275HX (24 cores), measured while other builds were running, so
expect the same order of magnitude rather than the same digits. Linux CI runners are
slower and noisier; the weekly Benchmark workflow publishes theirs.
Query
Mean
Median
single letter (a)
1.06 ms
1.01 ms
short prefix (fir)
21.3 ms
20.4 ms
app name (visual studio)
22.6 ms
22.0 ms
multi-word (budget report meeting)
19.2 ms
18.9 ms
no match anywhere (qzxvkj)
16.1 ms
16.0 ms
keyword: web (g ...)
0.003 ms
0.003 ms
keyword: bookmarks (b ...)
0.91 ms
0.70 ms
keyword: clipboard (cb ...)
0.28 ms
0.25 ms
keyword: files (f ...)
11.7 ms
10.9 ms
keyword: workflow (ddg ...)
0.004 ms
0.004 ms
path browse (~/Documents/rep)
6.8 ms
6.6 ms
calculator
22.0 ms
21.0 ms
unit conversion
11.7 ms
10.5 ms
startup: reload of every plugin (5,000 apps, 20,000 files walked on disk, 10,000 bookmarks, 200 clipboard entries)
103 ms (first run 136 ms)
100 ms
What the numbers say: everything that is routed to one plugin by a keyword is far
under the 16 ms line, but a global query over a 100,000-file index costs 16 to 23 ms
on this machine, because the files plugin scores every file name on every keystroke.
The files plugin caps its index at 100,000 entries, so this is the worst case; most
home folders hold far fewer. It is the number to watch if the files plugin changes. The startup row is a worst case for the walk: the 100,000-entry index
used by the query rows is injected rather than walked, because creating that many files
on every run would measure the disk.
Who. The maintainer, plus anyone with a machine from the matrix below. One
person can cover several rows with VMs, but Wayland, Apple silicon and a
physical Windows machine are worth real hardware.
When. Before every release that changes behaviour (any feat: or fix:
commit since the last tag), on the release build, not a development build: use
the installers from the draft release or the bundle artifacts of the commit.
A docs-only release needs nothing. A release that changed one area can run only
that area plus the smoke test (rows marked S) on every OS.
How.
Open a new issue from the
Release test report
template, one per OS and version. It asks for the build, the machine and
the IDs of failing rows.
Use a fresh user account or VM snapshot for the install rows, and a normal
one for the rest. Back up config.toml first; the plan edits it.
Work through the tables for that OS. Record only the failures and the
rows you could not run, with the ID (for example CLP-4), what happened and
a link to the bug you filed.
Rate each failure with the severity below. Any S1 blocks the release; an
S2 needs a fix or an explicit waiver from the maintainer in the report.
Safety. Never test destructive rows on a machine you care about. Use a
scratch folder for file operations, and a throwaway process for kill. Restart,
shut down, log out and empty trash are checked only as far as their
confirmation prompt, then cancelled.
Also check a machine with a non-US keyboard layout
W11
Windows 11
Primary Windows target
MI
macOS on an Intel Mac (11+)
Universal build
MA
macOS on Apple silicon
Universal build; Rosetta not needed
UX
Ubuntu 22.04 or 24.04 on GNOME, X11 session
.deb
UW
Ubuntu on GNOME, Wayland session
.deb, sevak --setup-hotkey
FW
Fedora (current) on GNOME, Wayland session
.rpm; AppIndicator extension needed for the tray
Optional extras worth a smoke test when available: KDE Plasma (X11 and
Wayland), a second monitor with a different scale factor, Windows with
high-contrast on, and an account without administrator rights.
In the tables, OS lists where a row applies (all is every machine
above; W is both Windows versions, M both Macs, L the three Linux
setups). Auto says what the automated tests already cover, so a tester
knows what is checked by hand because it cannot be checked by a machine.
Sev is the severity if the row fails. Rows marked S are the smoke test.
Download the NSIS -setup.exe from the release. Run it as a standard user
SmartScreen warning is the only obstacle ("More info", "Run anyway"); installs without administrator rights; Start menu entry; Sevak starts
Bundle job builds it; install itself is manual
S1
INS-2
W
Install the .msi as administrator
Installs per machine; Start menu entry for all users; Sevak starts
Bundle job builds it
S2
INS-3 S
M
Open the .dmg, drag Sevak to Applications, open it. Use "Open Anyway" in Privacy & Security
One-time Gatekeeper prompt; menu-bar icon (no Dock icon); the launcher opens
Bundle job builds it
S1
INS-4 S
L
sudo apt install ./Sevak_*.deb (Ubuntu) or sudo dnf install ./Sevak-*.rpm (Fedora). Start from the application menu
Installs with its dependencies; entry in the menu with a "Show or hide" action; Sevak starts
Bundle and Fedora jobs build and test; install is manual
S1
INS-5
L
Make the AppImage executable and run it (with FUSE 2, then with --appimage-extract-and-run)
Starts both ways
Bundle job builds it
S2
INS-6 S
all
Launch Sevak for the first time with no config folder
config.toml is created with comments; launcher opens with the default shortcut; no error dialog
Config defaults are unit-tested
S1
INS-7
all
Install the previous release, change settings, use clipboard history and snippets, then install this release over it
Settings, usage ranking, clipboard history and snippets survive; the launcher works at once
Config round-trips are unit-tested; upgrades are manual
S1
INS-8
all
Turn start at sign-in off and Save, then uninstall (Settings > Apps on Windows; delete the app on macOS; apt remove or dnf remove)
Sevak disappears from the menus and stops; config/data remain (see Files and data); this account's startup entry is absent. Also test Windows owned-entry cleanup with startup still on (STA-7).
Registration removal helpers; uninstall itself is manual
Run these against the branded Windows installer. They remain manual checks
because packaging CI builds the installers but does not exercise their UI or
installation behaviour on a real desktop.
ID
OS
Scenario
Steps
Expected
Sev
INS-A1
W
Fresh, per user
Run the installer as a standard user on a clean account
No administrator prompt; installed under the user's profile; Start menu and uninstall entry for that user only
S1
INS-A2
W
Fresh, per machine
Run the machine-wide installer as administrator
Installed for all users; another account sees the Start menu entry
S2
INS-A3
W
Upgrade
Install release N, run it, then install N+1 over it without uninstalling
Sevak (running or not) is replaced; no duplicate Start menu entries; no second uninstall entry; settings kept
S1
INS-A4
W
Downgrade
Install N+1, then run the installer of N
Either refuses with a clear message or installs N cleanly; config written by N+1 still loads or is reported, never silently reset
S2
INS-A5
W
Per-user to per-machine
Install per user, then install per machine
The second install detects the first (or the two do not clash); one working Sevak remains
S2
INS-A6
W
Silent install and uninstall
Run the installer with its silent switch from a script; then the silent uninstall
No UI; exit code 0; installed and removed correctly
S2
INS-A7
W
Running while upgrading
Leave Sevak running (tray icon) and upgrade
The installer closes it or asks; the new version starts afterwards; the Win+Space hook is released while it is closed
S2
INS-A8
W
Repair
Delete the installed executable, then repair or reinstall
Use disposable accounts or VM snapshots. Record the OS, installer format, install
scope and whether the test was an actual sign-out/sign-in or a restart. Passing
unit, integration or package-build tests alone does not complete these cases.
ID
OS
Steps
Expected
Auto
Sev
STA-1 S
W
Fresh EXE and MSI installs, first with startup unchecked, then checked
Sevak appears in Start → All apps and Settings → Apps → Installed apps in both cases. Startup defaults off; opting in adds a per-user Startup apps entry and General shows on.
Installer source contracts and native registration tests
S1
STA-2 S
all
Enable in General, Save, quit Sevak, restart and sign in; repeat after turning it off and saving
When on: one tray/menu-bar instance, launcher hidden, shortcut works. When off: no automatic process. On macOS/Linux use Settings after installation; no setup checkbox is expected.
Settings save/reopen and background command tests; restart is manual
S1
STA-3
W
Install for all users and opt in as account A; sign in as B. Repeat installation where UAC uses another administrator's credentials
Only the desktop account that chose startup is opted in; no machine-wide Run entry or automatic opt-in for the administrator/B.
User-scope installer contracts; real account/UAC behavior is manual
S1
STA-4
W, L
Enable and Save, disable Sevak through OS startup controls, then run Sevak manually and save an unrelated setting
OS disable is preserved; General warns that startup is disabled outside Sevak. Re-enable using the OS or off+Save then on+Save in Sevak.
Startup state preservation and UI warning tests
S2
STA-5
all
On a disposable profile, deny writes to startup registration, then try to enable/disable from General
Save displays an actionable error, keeps the prior persisted choice/registration, and does not show Saved. Fix access and retry; it succeeds.
Transaction rollback and UI failure/retry tests
S1
STA-6
W
Upgrade an opted-in and an opted-out install interactively and silently; repeat with /AUTOSTART=on\|off (EXE) or SEVAK_AUTOSTART=on\|off (MSI)
Unchanged/unspecified startup is preserved; explicit choice updates the invoking user's setting. Start-now remains independent. Service-account deployment never opts all users in.
Installer source contracts; installed upgrade is manual
S2
STA-7
W
Uninstall a copy with startup enabled; repeat with the entry pointing to a different Sevak install
Removes only this executable's entry for the invoking user. The other install's entry is kept. Upgrade/scope migration preserves the chosen startup behavior.
Ownership comparison/removal tests; uninstall is manual
S2
STA-8
M, L
Enable startup after installing to Applications/a permanent AppImage path; inspect the entry and sign in again
User LaunchAgent or XDG autostart starts the installed path with background arguments. Linux desktop supports XDG autostart; no temporary AppImage mount path is stored.
Native entry format/path tests; desktop behavior is manual
S2
STA-9
W
Move an install with an enabled entry, then with a Windows-disabled entry and custom --config argument; repeat an upgrade with no Run entry and an old saved opt-in
Existing registration follows the new executable while retaining arguments and disable state. An unchecked interactive choice with a missing entry saves off. Explicit preference/refresh happens before automatic launch.
Refresh fixtures, installer choice contracts and compiled MSI sequencing checks
S2
STA-10
W, L
In a disposable Windows VM, reproduce the old machine-wide Sevak Run entry; on Linux, use a minimal [Desktop Entry] with Hidden=true
Windows reports the migration error and leaves machine-wide startup untouched; administrator cleanup is clearly described. Linux recognizes the minimal override as disabled and an explicit opt-in can re-enable it.
Legacy command and Hidden-only fixtures; real OS integration is manual
Menu: Show, Settings, Reload index, Check for updates, Quit
None
S2
TRY-3
all
Choose each item
Show opens the launcher; Settings opens Settings; Reload index re-scans (a newly created file or installed app becomes searchable); Check for updates reports a result; Quit exits and removes the icon
None
S2
TRY-4
FW
Without the AppIndicator extension
No icon, but Sevak still runs and the hotkey works; with the extension enabled the icon appears
None
S3
TRY-5
L
sevak --settings, sevak --toggle, sevak --quit from a terminal
Clipboard history is opt-in. Turn it on under Settings > Plugins or with
[clipboard] enabled = true, then use Reload index.
ID
OS
Steps
Expected
Auto
Sev
CLP-1 S
all
Copy text in another app; type cb
The copied text is the top row; what was on the clipboard before turning the feature on is not there
History logic: about 60 unit tests
S1
CLP-2
all
Copy an image (screenshot or from a browser); cb, then cb image
A thumbnail row and a grid view; Enter pastes the image where supported
Image storage tested; capture is manual
S2
CLP-3
all
Copy files in the file manager
Recorded as a file list; pasting restores them
Tested with mock
S2
CLP-4 S
W, M
Copy a password from a password manager that marks it secret (1Password, KeePassXC, Bitwarden desktop)
Never appears in cb; the file clipboard-history.json does not contain it
The sensitive flag path is unit-tested; real managers are manual
S1
CLP-5
L
Add the password manager to ignore_apps; copy from it
Not recorded (Linux has no secret marker)
ignore_apps tested
S1
CLP-6
all
Copy text longer than max_item_bytes (64 KiB)
Not recorded
Tested
S3
CLP-7
all
Pick a text entry: Enter pastes into Notepad or TextEdit, Ctrl+C copies without pasting; then type cb clear and press Enter on the Clear clipboard history row
As labelled; clearing (no further prompt) empties the history and the image files
Tested
S2
CLP-8
all
Copy 250 distinct texts
The list stops at max_items (200); images over the 500 MB total prune oldest first
Trim logic tested
S3
CLP-9
L, M
Inspect the permissions of clipboard-history.json
Readable only by the user (0600)
Unit-tested
S2
CLP-10
all
Turn the feature off
Nothing more is recorded; no clipboard polling continues
Status is honest: items marked Unknown have never been assessed and need a
first pass; record the result and file bugs.
ID
OS
Steps
Expected
Auto
Status
Sev
A11-1 S
all
Use Sevak without a mouse: open with the shortcut, search, run, open the action panel, open Settings with sevak --settings, tab through every Settings page
Everything reachable by keyboard; focus always visible; no keyboard trap
Contrast and ARIA basics only
Partly known
S2
A11-2
W
Windows High Contrast on; and a dark/light built-in theme
Text and selection readable; the built-in themes keep text at WCAG AA contrast
themes.test.ts checks AA for built-ins; High Contrast mode itself is untested
Unknown
S2
A11-3
W
NVDA or Narrator: type a query and arrow through results
The selected result is announced (the search box uses aria-activedescendant over a listbox)
None
Unknown
S2
A11-4
M
VoiceOver: same
Same
None
Unknown
S2
A11-5
L
Orca: same
Same
None
Unknown
S2
A11-6
all
Increase the font size to the maximum in Settings; OS text scaling at 150 percent
No clipped text; window grows or scrolls
None
Unknown
S3
A11-7
all
Reduce motion in the OS
Animations stop (Sevak's own transitions honour prefers-reduced-motion)
None
Partly known
S4
A11-8
all
Settings: every control has a label (switches announce on/off)
When a feature is added or a platform quirk is found, add a row with a new ID in
the right table and say what, if anything, already tests it. When a manual row
becomes automatic, change its Auto note and consider dropping it from the
smoke test. Rows are never renumbered, so reports from older releases stay
meaningful.