Skip to content

Commit 62841f9

Browse files
committed
test: fail a11y tests if warnings are emitted by components
1 parent 9edf83d commit 62841f9

File tree

1 file changed

+39
-4
lines changed

1 file changed

+39
-4
lines changed

test/nuxt/a11y.spec.ts

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { mountSuspended } from '@nuxt/test-utils/runtime'
33
import type { VueWrapper } from '@vue/test-utils'
44
import 'axe-core'
55
import type { AxeResults, RunOptions } from 'axe-core'
6-
import { afterEach, describe, expect, it, vi } from 'vitest'
6+
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from 'vitest'
77

88
// axe-core is a UMD module that exposes itself as window.axe in the browser
99
declare const axe: {
@@ -48,6 +48,35 @@ async function runAxe(wrapper: VueWrapper): Promise<AxeResults> {
4848
return axe.run(container, axeRunOptions)
4949
}
5050

51+
// --- Console warning assertion --------------------------------------------------
52+
// Fail any test that emits unexpected console.warn calls. This catches issues
53+
// like missing/invalid props that would otherwise silently pass.
54+
let warnSpy: MockInstance
55+
56+
// Patterns that are expected and safe to ignore in the test environment.
57+
const allowedWarnings: RegExp[] = [
58+
// vue-i18n logs this when <i18n-t> is used outside a component-scoped i18n;
59+
// it falls back to the global scope and still renders correctly.
60+
/\[intlify\] Not found parent scope/,
61+
]
62+
63+
beforeEach(() => {
64+
warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
65+
})
66+
67+
afterEach(() => {
68+
// Collect unexpected warnings
69+
const unexpected = warnSpy.mock.calls.filter(
70+
args => !allowedWarnings.some(re => re.test(String(args[0]))),
71+
)
72+
warnSpy.mockRestore()
73+
74+
if (unexpected.length > 0) {
75+
const msgs = unexpected.map(args => args.map(String).join(' ')).join('\n')
76+
throw new Error(`Test emitted unexpected console.warn:\n${msgs}`)
77+
}
78+
})
79+
5180
// Clean up mounted containers after each test
5281
afterEach(() => {
5382
for (const container of mountedContainers) {
@@ -65,9 +94,15 @@ vi.mock('vue-data-ui/vue-ui-xy', () => {
6594
VueUiXy: defineComponent({
6695
name: 'VueUiXy',
6796
inheritAttrs: false,
68-
setup(_, { attrs, slots }) {
69-
return () =>
70-
h('div', { ...attrs, 'data-test-id': 'vue-ui-xy-stub' }, slots.default?.() ?? [])
97+
// Declare the props VueUiXy receives so they don't fall through to attrs.
98+
// Spreading `dataset` onto a DOM element triggers a Vue warning because
99+
// HTMLElement.dataset is a read-only getter.
100+
props: {
101+
dataset: { type: Array, default: () => [] },
102+
config: { type: Object, default: () => ({}) },
103+
},
104+
setup(_, { slots }) {
105+
return () => h('div', { 'data-test-id': 'vue-ui-xy-stub' }, slots.default?.() ?? [])
71106
},
72107
}),
73108
}

0 commit comments

Comments
 (0)