Manual browser test plan
August 16, 2026 · View on GitHub
Automated coverage in this repo is npm run test (Vitest, four files under
src/) plus npm run typecheck, npm run lint, and npm run build. None of
those load WASM or run a model. Everything below is what a person has to do in
a browser before calling a release good. The Web SDK's own Playwright suite
lives in the monorepo at bindings/web/tests/browser/, not here.
Setup
npm ci, thennpm run dev(ornpm run build && npm run preview).- Open
http://localhost:3000. Uselocalhost, not127.0.0.1: they are different origins, so OPFS contents and localStorage do not carry across. - In the console, confirm
crossOriginIsolated === trueandtypeof SharedArrayBuffer !== 'undefined'. - Keep DevTools open on Console and Network for the whole pass.
Some checks need a model on disk. Download one small LLM (the model sheet lists sizes) and one Sherpa speech bundle before starting, or run section B first.
A. Boot
- The boot screen paints immediately, before the bundle finishes loading, and its status line advances through "Starting the on-device runtime", "Preparing text generation", "Preparing speech", "Checking available models".
- The shell appears: top bar with the menu button, brand, model slot, new-chat, theme, and settings buttons; drawer with Assistant, Talk, Choose model, Downloads, Settings, Advanced.
- Console shows
[RunAnywhere] llamacpp backend registered: cpu(orwebgpu) and[RunAnywhere] onnx/sherpa backend registered: …. - Console shows
[RunAnywhere] SDK initialized, version: …and aModel registry: registered=…, downloaded=…, available=…line. window.__RUNANYWHERE_AI_READY__.ready === true, and<html>carriesdata-runanywhere-ai-ready="true"withdata-runanywhere-ai-step="interactive".- No unexpected console errors.
B. Model catalog and download
- Open the model sheet from the drawer's "Choose model" or the chat toolbar.
- Rows list a framework, a size, and a state badge. Models the browser cannot run are visibly gated rather than offered.
- "Add from Hugging Face" is present in the sheet footer.
- Download the smallest LLM. Progress updates, then the row reads as on-device.
- Go to Downloads: the model is listed, with the correct size, and storage usage reflects it.
- Reload the page. The model still reads as on-device (OPFS survived).
C. Assistant
- With no model loaded, the Get Started overlay covers the composer.
- Load a model from the sheet. The overlay clears and the toolbar names the model.
- Four suggestion chips render on the empty state. Clicking one prefills the composer and focuses it. It does not send.
- Send is disabled on an empty composer. Enter submits, Shift+Enter inserts a newline.
- Send a prompt. Tokens stream in, then the turn completes with metrics.
- Toggle Tools on and ask something that needs one of the three demo tools (weather, current time, calculator). The tool call and its result render.
- New chat clears the thread. The drawer's Recent list keeps the previous one, reopening it restores the messages, and deleting it removes it.
- Reload with a saved chat selected. It restores from IndexedDB.
D. Routing
- Each surface changes the URL fragment (
#/vision,#/benchmarks, and so on). - Reloading on a fragment restores that surface, not the assistant.
- Browser Back and Forward move between visited surfaces.
- A drilled-into surface (anything under Advanced) shows a Back button in its toolbar that returns to where you actually came from.
- Editing the fragment in the address bar navigates.
- An unknown fragment falls back to the assistant.
E. Image and live camera
- Open Image & Live. With no VLM loaded, controls are disabled and the engine notice explains why.
- Load a VLM. Load an image from disk, or start the camera and capture a frame.
- Describe the frame. Output streams in and Cancel stops it mid-stream.
- Frame metadata and timing render after the first capture.
F. Speech
Each of these needs the matching Sherpa model downloaded and loaded.
- Transcribe: record from the microphone and confirm streaming partials, then a final transcript. Drop an audio file and confirm the batch path produces a transcript. Clear empties the output.
- Read aloud: enter text, adjust the rate, press Speak, and confirm audio plays. Stop interrupts it mid-utterance.
- Voice activity: start listening and confirm the speech pill flips between speech and silence, the confidence readout moves, and the event log fills.
- Talk: run setup, start a session, speak, and confirm the transcript, the assistant response, and spoken output. Interrupt cuts the reply off.
G. Documents
- Pick an embedding model and an LLM. Download either from its row if missing.
- Drop in a
.txt,.md, or.jsonfile. It appears in the list and indexes. Drop an unsupported extension and confirm it is rejected with a reason, not silently ignored: a drop bypasses the input'sacceptfilter. - Ask a question. The answer streams and lists the retrieved sources.
- "Clear all" empties the index. The index is session-only, so a reload clears it too. Confirm the UI says so rather than implying persistence.
H. Solutions and benchmarks
- Solutions lists the two packaged workflows, Voice agent and Document Q&A. Running one with its models present produces a result; running one without them fails with a readable per-solution message rather than a silent no-op. The per-solution recheck control re-probes engine availability.
- Benchmarks runs one prompt at three token budgets (Short 50, Medium 256, Long 512) and charts time-to-first-token and tokens per second for each. With no model loaded, the run buttons are disabled and the view says to load one.
I. Downloads and storage
- The panel names the storage backend (private browser storage, or a chosen folder) and shows per-site usage against the quota.
- "Choose Storage Folder" opens the directory picker. Cancelling is a no-op.
- If a chosen folder lost permission, the re-authorize control restores access.
- Delete removes a model immediately, with a toast and no confirmation prompt. Afterwards the model sheet shows it as not downloaded and usage drops.
- "Clear Caches" also runs immediately, with a toast.
- A paused or failed transfer offers Delete so partial bytes can be reclaimed, and an in-flight one offers Cancel.
J. Settings
- Temperature is a slider from 0 to 2 in steps of 0.1, defaulting to 0.7.
- Max Tokens defaults to 10000 and steps by 500 between 500 and 20000. The minus and plus buttons disable at the bounds.
- System Prompt and Thinking Mode render. Thinking Mode is off by default.
- API Key is a password field, Base URL is a URL field. "Apply & Reinitialize" restarts the runtime; a bad endpoint reports the failure and restores the previous runtime rather than leaving the app broken.
- The Hugging Face token field reports Configured or Not set, and Clear resets it.
- Analytics is a read-only state row describing the SDK environment. It is not a toggle.
- SDK Version matches the installed
@runanywhere/web, and Documentation links tohttps://docs.runanywhere.ai. - Change a generation setting, reload, and confirm it persisted. Confirm the API key did not.
K. Degraded and unavailable states
- Segmentation and Diarization always render the unavailable placeholder, and the placeholder names the SDK verb each would call. No browser engine publishes those capabilities.
- Block one WASM artifact in DevTools and reload. The affected engine reports as unavailable, the drawer footer reads "On-device engine unavailable", the picker gates exactly the rows that need it, and the rest of the app stays navigable. Retry recovers without a page reload once the block is lifted.
- Serve the built bundle without COOP/COEP headers. The isolation service worker installs, reloads once, and the app comes up isolated. It does not reload in a loop.
L. Theme and layout
- The theme button toggles light and dark, and the choice survives a reload.
- With no stored choice, the app follows the OS preference, including a change made while the tab is open.
- At a 390px viewport the drawer is a scrim overlay, Escape closes it, and nothing overlaps the composer.
M. Console audit
Collect every console error and network failure from the pass and classify each as expected (a capability probe that legitimately fails on this browser) or a real defect. Attach screenshots for anything that is not obviously one or the other.