web-testing-vue-test-utils-4ea599 — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited web-testing-vue-test-utils-4ea599 (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Quick Guide:mount()renders a component with its children;shallowMount()stubs them all and is the exception rather than the default. Query withget()when the element must exist andfind()when it may not, usingdata-testattributes rather than classes. Every DOM-updating method returns a promise and is awaited; anything Vue's reactivity does not track — a request, a timer — needsflushPromises()as well. A component's environment arrives through theglobalmounting options.
Detailed Resources:
get/find/findAll, trigger, setValueflushPromises, nextTick, debounced input, async setup() under Suspenseglobal seams: plugins, stubs, mocks, provide — and a custom mount that composes themmount it and drive the wrapper;real setup scope; examples/composables.md. Calling a composable outside a component works only for ones that register no lifecycle hooks and inject nothing.
Suspense, or it neverresolves; examples/async.md.
<critical_requirements>
Await every DOM-updating method — `trigger()`, `setValue()`, `setProps()`, `setData()`. Each returns a promise that resolves after Vue has flushed, so an un-awaited call leaves the assertion looking at the pre-update DOM.
Call `flushPromises()` after anything Vue's reactivity does not own — a request, a timer callback, a promise chain started in onMounted. Awaiting the interaction flushes Vue's queue and nothing else.
Reach for `mount()` first, and treat `shallowMount()` as the exception. Stubbing every child changes how the component behaves, so a shallow test passes for a tree that does not render.
Select with `data-test` attributes. Classes and ids belong to styling and move under a restyle; a data-test attribute is a stated contract that a reader can see is load-bearing.
Supply the component's environment through the `global` mounting options — plugins, stubs, mocks, provide. That is the seam the library gives you, and it keeps the arrangement visible at the mount rather than hidden in module-level substitution.
</critical_requirements>
Auto-detection: @vue/test-utils, mount, shallowMount, VueWrapper, DOMWrapper, wrapper.get, wrapper.find, findAll, findComponent, findAllComponents, getComponent, trigger, setValue, setProps, setData, flushPromises, emitted, enableAutoUnmount, attachTo, renderStubDefaultSlot, config.global
Applies to:
Handled elsewhere:
describe/it, the assertion API, module substitution, mockfunctions and fake timers all belong to whatever runs the file
global.plugins, and how to configure it is settled by whoever owns the store
<philosophy>
Vue's DOM updates are asynchronous, and the library's API is shaped around that one fact. trigger, setValue, setProps and setData all return promises for the same reason: the effect of the change is not observable on the line after it. Most flaky Vue component tests are a missing await on one of those four.
Two async queues, two tools. Awaiting a wrapper method flushes Vue's own render queue. flushPromises() drains the microtask queue, which is where a resolved request or a settled promise chain is waiting. A component that fetches on mount needs the second even though nothing was triggered.
Stubbing is a dial, not a switch. shallowMount is the far end of it — every child replaced, slots inert, integration coverage gone. Stubbing the one heavy child by name keeps the rest of the tree real and usually solves the actual problem, which is a chart library or a network call rather than depth.
</philosophy>
<patterns>
import { mount, shallowMount } from "@vue/test-utils";
// Default: children render, slots work, events bubble
const wrapper = mount(TodoList, {
props: { todos: [{ id: 1, text: "Test", done: false }] },
});
// Every child stubbed - reach for this only when depth itself is the problem
const shallow = shallowMount(TodoList, { props: { todos: [] } });
// Usually better than shallow: stub the one child that misbehaves
const selective = mount(Dashboard, {
global: { stubs: { HeavyChartWidget: { template: "<div />" } } },
});Full code: examples/core.md
get* throws with a useful message when the element is missing; find* returns an empty wrapper you then check with .exists(). The same split applies to getComponent / findComponent.
const input = wrapper.get('[data-test="search-input"]'); // must exist
expect(wrapper.find('[data-test="error"]').exists()).toBe(false); // may not exist
expect(wrapper.findAll('[data-test="result"]')).toHaveLength(3);
expect(wrapper.getComponent(ChildComponent).props("message")).toBe("Hello");Using find() where get() was meant is the quiet failure: the empty wrapper satisfies nothing, and the assertion after it reports a confusing type error rather than "element not found".
Full code: examples/core.md
await wrapper.get('[data-test="email"]').setValue("[email protected]");
await wrapper.get('[data-test="form"]').trigger("submit.prevent");
expect(wrapper.emitted("submit")).toBeTruthy();
expect(wrapper.emitted("submit")![0]).toEqual([{ email: "[email protected]" }]);emitted() accumulates across the whole test, so index into it rather than asserting on the array as a whole after several interactions.
Full code: examples/core.md
import { mount, flushPromises } from "@vue/test-utils";
const wrapper = mount(UserProfile, { props: { userId: 1 } });
expect(wrapper.find('[data-test="loading"]').exists()).toBe(true);
await flushPromises(); // the request settles, then Vue re-renders
expect(wrapper.get('[data-test="user-name"]').text()).toBe("John Doe");flushPromises() drains microtasks only. A setTimeout needs the runner's fake timers advanced first, and then flushPromises() for whatever the callback started.
Full code: examples/async.md
Mount a component whose only job is to call the composable, so lifecycle hooks and inject work as they do in production.
function withSetup<T>(composable: () => T) {
let result!: T;
const wrapper = mount(
defineComponent({ setup: () => ((result = composable()), () => null) }),
);
return { result, unmount: () => wrapper.unmount() };
}
const { result, unmount } = withSetup(() =>
useLocalStorage("draft", "initial"),
);Where the composable is inseparable from its UI, render a small template around it and assert on the DOM instead — the reactivity is then covered rather than assumed.
Full code: examples/composables.md
Four seams, each for a different kind of dependency, all set at the mount and all settable project-wide through config.global.
mount(Component, {
global: {
plugins: [
/* anything installed with app.use() - a store, a router, an i18n instance */
],
stubs: { RouterLink: true, HeavyChart: { template: "<div />" } },
mocks: { $t: (key: string) => key }, // global properties the template reads
provide: { [THEME_KEY]: { theme: ref("light") } }, // what an ancestor would provide
},
});Wrap these in a custom mount once, so a test states only what makes it different.
Full code: examples/mocking.md
</patterns>
<red_flags>
Breaks at runtime:
trigger(), setValue(), setProps() or setData() without await — the assertion reads the DOM before Vue has flushed, so the test passes or fails by timingflushPromises() after a request resolves — the loading branch is still rendered when the assertion runssetData() on a component written with the Composition API — it only reaches an Options API data() function, so the call silently changes nothingsetup() mounted directly instead of inside Suspense — it never resolves and the wrapper stays on the fallbackisVisible() without attachTo: document.body — the element is not in a rendered document, so visibility cannot be computed correctlytrigger("click") on a disabled element — the browser drops it, and so does the library; that is correct behaviour rather than a bug to work aroundSurprising behaviour:
shallowMount stubs every child including ones from a component library, so a test can pass against a tree that renders nothingfind() returns an empty wrapper rather than throwing, which turns a missing element into a confusing error one line lateremitted() accumulates for the lifetime of the wrapper — asserting a length after several interactions counts all of themsetValue() applies only to <input>, <textarea> and <select>; on anything else it is a no-optrigger() already awaits nextTick, so an extra await nextTick() after it is redundantwrapper.vm to call a method or read state couples the test to the implementation; drive the component through the DOM insteadattachTo stays in the document until unmounted — register enableAutoUnmount once rather than remembering per test</red_flags>
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.