Header Logo

How to Test a Realtime Talking Avatar in Vue 3 with Vitest and Playwright

How to Test a Realtime Talking Avatar in Vue 3 with Vitest and Playwright

Test a Vue 3 realtime avatar with Vitest for state logic and Playwright for WebRTC, media, and permissions.

Introduction


Testing a realtime talking avatar is different from testing a normal Vue component. You are not just asserting DOM state; you are dealing with asynchronous media, WebRTC connection setup, reactive UI state, playback timing, and often a remote service that produces a live video stream. That combination creates a common trap: unit tests pass, but the integration breaks in the browser when microphone permission, session negotiation, or video attachment fails.


In this post, I’ll show a practical way to test a Vue 3 avatar client with Vitest for fast component-level checks and Playwright for browser-level integration. By the end, you should be able to verify that your app can:


  • create and render an avatar session

  • attach a live video stream to a <video> element

  • handle session lifecycle errors and reconnects

  • cover the real browser behavior that unit tests cannot simulate


The examples assume a Protoface-style avatar backend and a Vue 3 app using the Composition API.


What to test where


For realtime avatars, split the problem by boundary:


  • Vitest for pure UI logic: reactive state, computed labels, store transitions, and thin wrappers around session initialization.

  • Playwright for browser integration: permissions, media attachment, autoplay constraints, and verifying that the UI behaves when a live stream exists.


That split matters because a video avatar is not a deterministic local widget. The browser has rules around autoplay, user gestures, and media track lifecycles. If you only unit test the Vue layer, you can miss all of that.


Model the avatar client as an adapter


The first step is to keep the Vue app from knowing too much about the transport. Don’t call the REST API or SDK directly inside random components. Wrap avatar session creation and stream attachment behind a small adapter, then inject or mock that adapter in tests.


A minimal composable might look like this:


import { ref } from 'vue'

}
import { ref } from 'vue'

}
import { ref } from 'vue'

}


This is intentionally simple. In a real app, your client may come from a browser SDK, a backend-issued session token, or a wrapper around an iframe embed. The important part is the seam: the Vue layer depends on an abstract client, not hard-coded network calls.


Vitest: test state transitions, not media playback


Vitest is the right tool for testing the logic around avatar state. Mock the client and assert the order of transitions. You should not try to spin up real WebRTC media in Vitest; that creates slow, brittle tests that usually fail for environmental reasons.


import { describe, it, expect, vi } from 'vitest'

})
import { describe, it, expect, vi } from 'vitest'

})
import { describe, it, expect, vi } from 'vitest'

})


A few things are worth testing here:


  • Loading state before the session is created.

  • Error state when session creation or stream attachment fails.

  • Cleanup when the component unmounts or the user stops the session.

  • Retry behavior if your app automatically reconnects after a transient failure.


In practice, the best unit tests assert the state machine, not the pixel output. If your component renders “connecting…”, “live”, or “retrying”, those are good candidates for Vitest. If you need to know whether a real stream appears in the browser, move that to Playwright.


Playwright: verify the browser behavior that actually breaks


Playwright is where you test the integration between the Vue app and the browser. For avatars, the highest-value checks are:


  1. the user clicks a start button and the app reaches a connected state

  2. the video element receives a stream and becomes visible

  3. the app handles denied permissions or failed session setup cleanly


Rather than depending on a real avatar session in every test run, you usually want to stub the backend boundary. You can do that at the network layer, or expose a test-only route in your app that uses a mocked client.


A simple Playwright test could assert that your UI wires the stream to the video element:


import { test, expect } from '@playwright/test'

})
import { test, expect } from '@playwright/test'

})
import { test, expect } from '@playwright/test'

})


If your app uses real media permissions, configure the browser context explicitly. Otherwise the test may fail only in CI, which is the worst time to discover autoplay or microphone issues. In Chromium-based tests, a fake media stream and permission grant are often enough to exercise the UI path without requiring a live human.


For failure cases, test the messaging users actually see. For example, if session creation returns 401 because an API key is invalid or an embed origin is not allowlisted, the UI should tell the developer what went wrong instead of sitting on “connecting” forever.


Practical gotchas with realtime avatars


There are a few recurring failure modes that are worth encoding into tests:


  • Autoplay restrictions: some browsers require a user gesture before playback starts, especially if audio is involved.

  • Reactive timing: Vue may render the video element after your async session resolves, so make sure you wait for the DOM before assigning srcObject.

  • Track lifecycle: a disconnected stream can leave a stale video element unless you clear srcObject on stop.

  • Error surfaces: transport errors, permission errors, and session authorization errors should not all collapse into the same generic message.


One useful pattern is to keep your video attachment logic idempotent. If the session reconnects and you receive a fresh stream, replacing srcObject should be safe. Likewise, stopping a session should be able to run more than once without throwing.


How Protoface fits into the test strategy


This is exactly the kind of boundary Protoface is built to support. In a Vue app, you usually do not want your tests to depend on a live production API key or a real avatar session. Instead, keep the browser-side behavior local and test the real service at the integration boundary: the REST API for session creation, or the browser-facing surface you use to render the avatar.


If you need to exercise session creation outside the UI, the REST API is a straightforward target for smoke tests from CI. Use short-lived test credentials and assert only the contract you care about. For example:


curl -X POST https://api.protoface.com/v1/sessions \
-d '{"avatar_id":"avt_test","voice":"en-US"}'
curl -X POST https://api.protoface.com/v1/sessions \
-d '{"avatar_id":"avt_test","voice":"en-US"}'
curl -X POST https://api.protoface.com/v1/sessions \
-d '{"avatar_id":"avt_test","voice":"en-US"}'


The exact request fields will depend on the endpoint shape in the docs, but the testing principle stays the same: keep the Vue tests focused on UI and lifecycle, and keep the service contract tests narrow and explicit. If you are wiring a deeper integration, the docs at docs.protoface.com are the right reference for current request and response fields.


Recommended test matrix


If you are building a realtime avatar UI, a small test matrix goes a long way:


  • Vitest: state transitions, event handlers, cleanup, and error rendering.

  • Playwright: start/stop flows, video visibility, permission handling, and reconnect UX.

  • API smoke test: one happy-path session creation check against the backend contract.


That gives you high confidence without trying to simulate all of WebRTC in a unit test runner.


Conclusion


The main idea is simple: treat a realtime avatar as a distributed system problem, not a pure component problem. Use Vitest to test the Vue state machine, and use Playwright to verify the browser behavior around media, permissions, and playback. Keep the avatar client behind a thin adapter so you can mock it cleanly, and reserve end-to-end service checks for the backend boundary.


If you are implementing this with Protoface, start with the docs, wire a small adapter around the session API or browser integration, and then add the tests that protect the real failure modes you care about. The quickest path from there is usually a small working prototype plus one or two targeted integration tests, not a giant test harness.

Add a face to your AI.

No credit card needed.

Add a face to your AI.

No credit card needed.

Add a face to your AI.

No credit card needed.