Course outline

Vue / Intermediate: feature state and effect ownership / Lesson 12

Component testing and accessible user tasks

Verify public behavior through controlled inputs and events, then evaluate semantics, focus, and real interaction separately.

About 4 min readContent reviewed
On this page

Lesson goals

  • Choose unit, component, and browser checks by the claim.
  • Test labels, errors, and keyboard tasks alongside data flow.

Test the smallest boundary that can reveal the defect

A pure validation utility can run under Node, a component event needs a Vue-aware mounting environment, and actual focus or layout behavior may need a real browser. These checks answer different questions. A passing string function cannot establish that the template wires the handler correctly, and a screenshot cannot establish that a screen reader understands the form. Select representative user journeys and focused lower-level cases. Keep fakes faithful to the contract they replace without pretending they validate the actual backend. Report what was executed and what remains a documented framework expectation.

Assert the component’s event contract

Vue Test Utils for Vue 3 can mount a component in a compatible test environment. The example uses Vitest syntax and the earlier TitleForm component. Awaiting setValue and trigger lets the test observe the update sequence rather than asserting before Vue processes it. The case verifies trimming and the emitted event, not remote persistence. A second negative case should verify that invalid input emits nothing and exposes a useful error. These imports require configured dependencies and a DOM-like environment; they are not installed or run as part of this course authoring.

javascript An illustrative Vue Test Utils event test
import { mount } from "@vue/test-utils"
import { it, expect } from "vitest"
import TitleForm from "./TitleForm.vue"

it("emits a trimmed title", async () => {
  const wrapper = mount(TitleForm)
  await wrapper.get("input").setValue("  Notes  ")
  await wrapper.get("form").trigger("submit")
  expect(wrapper.emitted("save-title")).toEqual([["Notes"]])
  wrapper.unmount()
})

Requires compatible pinned Vue Test Utils, Vitest, SFC transformation, and DOM test setup. The expected emitted payload is Notes. This test was not executed because those framework dependencies are unavailable in the isolated authoring environment.

Expose the same meaning beyond visual appearance

Use native controls, associated labels, descriptive button names, and explicit error relationships. Do not rely on color alone to indicate invalid input. A component with several instances needs stable unique ids so labels and descriptions point to the right controls. Vue 3.5’s useId supports stable ids across server and client rendering under its documented application context. Automated accessibility tools can detect classes of problems but do not prove complete conformance or task usability. Check the actual journey with keyboard and assistive technology, including correction, submission, and navigation after an error.

Build a field with explicit error association

The field below accepts an error message and a required model value. The generated id is used consistently for the label and error description, while the model remains owned by the parent contract. defineModel is supported in the chosen baseline and avoids a mismatched private default by requiring the value. The component does not decide validation policy or persistence. Verify its resulting accessible name, invalid state, and description in the actual test environment. A correct-looking markup sample remains an implementation hypothesis until browser and assistive behavior are checked.

vue A Vue 3.5 field with stable ids and error semantics
<script setup>
import { useId } from "vue"
const model = defineModel({ type: String, required: true })
const props = defineProps({ error: { type: String, default: "" } })
const inputId = useId()
const errorId = `${inputId}-error`
</script>

<template>
  <label :for="inputId">Note title</label>
  <input :id="inputId" v-model="model"
    :aria-invalid="props.error ? 'true' : undefined"
    :aria-describedby="props.error ? errorId : undefined">
  <p v-if="props.error" :id="errorId" role="alert">{{ props.error }}</p>
</template>

Requires a parent v-model value and the documented Vue 3.5 compiler/runtime. No browser or accessibility engine was run for this SFC. The example states the intended relationship without claiming full accessibility from markup alone.

Control time, data, and interaction evidence

Tests should control async outcomes, fixture identity, and cleanup instead of depending on public services or long sleeps. When a test fails intermittently, preserve the first relevant evidence and distinguish a deterministic product defect from environment timing or shared data. Verify prop changes, emitted payloads, conditional removal, and repeated mounting where ownership matters. Keep visual comparisons tied to known viewport, fonts, locale, and browser conditions. A green test run supports only the configured cases and environment. Pair automation with focused task review so critical accessibility or integration gaps remain visible rather than hidden behind a pass percentage.

Common errors and fixes

An assertion runs before the update is processed

Why it happens
The test ignores asynchronous component updates.
How to fix it
Await the documented interaction and flush relevant work.
Prevent it next time
Use deterministic update boundaries instead of sleeps.

Repeated fields share one label target

Why it happens
A fixed HTML id is reused across instances.
How to fix it
Generate stable unique ids and verify associations.
Prevent it next time
Test more than one instance and SSR id behavior.

A passing accessibility scan is called complete access

Why it happens
Tool coverage is confused with the whole user task.
How to fix it
Review keyboard, focus, and assistive journeys explicitly.
Prevent it next time
State automated limits and manual evidence.

Check your understanding

1. The component test confirms an emitted title. Does it prove a successful server save?

Show worked solution

No. It verifies the component’s proposal under the harness. The parent/service integration, server validation, authorization, and durable outcome need their own evidence. Keep pending and confirmed UI states distinct.

2. A form error is red but has no text. What should the contract add?

Show worked solution

Provide a clear message associated with the relevant control, expose invalid state appropriately, and preserve a usable correction path. Verify with keyboard and assistive technology rather than assuming color communicates the problem to every user.

Key takeaways

  • A test boundary determines what a passing result means.
  • Semantics, focus, and errors belong to the public contract.
  • Automation complements real task verification.

Official references