Skip to content

Commit 5155d34

Browse files
committed
chore: eliminate all test warnings (2538 → 0)
- Fix createPlugin inject to suppress Vue warning when fallback exists - Add hasInjectionContext guard to useForm - Fix PopoverRoot non-namespaced context key - Namespace all test context keys with test: or v0: prefix - Wrap effectScope for composables using onScopeDispose - Assert expected warnings instead of silently swallowing - Update testing rules with zero-warnings policy
1 parent 8c13671 commit 5155d34

File tree

30 files changed

+354
-190
lines changed

30 files changed

+354
-190
lines changed

.claude/rules/testing.md

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { mount } from '@vue/test-utils'
2222
// Mock Vue DI when needed
2323
vi.mock('vue', async () => {
2424
const actual = await vi.importActual('vue')
25-
return { ...actual, provide: vi.fn(), inject: vi.fn() }
25+
return { ...actual, provide: vi.fn(), inject: vi.fn(), hasInjectionContext: vi.fn(() => true) }
2626
})
2727

2828
describe('composableName', () => {
@@ -80,6 +80,72 @@ describe('createFoo', () => {
8080

8181
For lifecycle-dependent composables, wrap in `effectScope()`.
8282

83+
## Zero Warnings Policy
84+
85+
Tests must produce **zero** `stderr` output. Run `npx vitest run --reporter verbose 2>&1 | grep -E '\[Vue warn\]|\[v0.*warn\]|\[v0:context\]'` to verify.
86+
87+
### Namespace keys
88+
89+
All string keys passed to `createContext()` or `createXContext({ namespace })` must contain `:`.
90+
Use `test:` prefix for test-only keys, `v0:` for production keys.
91+
92+
```ts
93+
// wrong — triggers [v0:context] namespace warning
94+
createContext('my-key')
95+
createFooContext({ namespace: 'custom' })
96+
97+
// right
98+
createContext('test:my-key')
99+
createFooContext({ namespace: 'test:custom' })
100+
```
101+
102+
### Composables that use `onScopeDispose`
103+
104+
If a composable calls `onScopeDispose` internally (e.g. `createCombobox`, `useVirtualFocus`), wrap calls in `effectScope()`:
105+
106+
```ts
107+
let scope: EffectScope
108+
109+
function setup(options = {}) {
110+
let ctx: ReturnType<typeof createCombobox>
111+
scope = effectScope()
112+
scope.run(() => { ctx = createCombobox(options) })
113+
return ctx!
114+
}
115+
116+
afterEach(() => { scope?.stop() })
117+
```
118+
119+
### Expected warnings
120+
121+
When a test intentionally triggers a warning (error paths, duplicate registration, etc.), capture it with `vi.spyOn` and **assert** it was called — never silently swallow:
122+
123+
```ts
124+
const spy = vi.spyOn(console, 'warn').mockImplementation(() => {})
125+
126+
// ... code that warns ...
127+
128+
expect(spy).toHaveBeenCalledTimes(1)
129+
expect(spy).toHaveBeenCalledWith(expect.stringContaining('expected message'))
130+
spy.mockRestore()
131+
```
132+
133+
### Vue DI mocks
134+
135+
When mocking `provide`/`inject` from Vue and the composable under test uses `hasInjectionContext()`, include it in the mock:
136+
137+
```ts
138+
vi.mock('vue', async () => {
139+
const actual = await vi.importActual('vue')
140+
return {
141+
...actual,
142+
provide: vi.fn(),
143+
inject: vi.fn(),
144+
hasInjectionContext: vi.fn(() => true),
145+
}
146+
})
147+
```
148+
83149
## Component Test Pattern
84150

85151
```ts

packages/0/src/components/AlertDialog/index.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,8 @@ describe('alert-dialog', () => {
802802
})
803803

804804
it('should support async confirm flow', async () => {
805+
const spy = vi.spyOn(console, 'warn').mockImplementation(() => {})
806+
805807
const isOpen = ref(true)
806808
let closeFn: (() => void) | undefined
807809

@@ -855,6 +857,9 @@ describe('alert-dialog', () => {
855857
await nextTick()
856858

857859
expect(isOpen.value).toBe(false)
860+
expect(spy).toHaveBeenCalledTimes(1)
861+
862+
spy.mockRestore()
858863
})
859864
})
860865
})

packages/0/src/components/Button/index.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,13 +658,18 @@ describe('button', () => {
658658
})
659659

660660
it('should serialize object values to JSON', () => {
661+
const spy = vi.spyOn(console, 'warn').mockImplementation(() => {})
662+
661663
const wrapper = mount(Button.Root, {
662664
slots: {
663665
default: () => h(Button.HiddenInput as any, { name: 'data', value: { foo: 'bar' } }),
664666
},
665667
})
666668

667669
expect(wrapper.find('input').attributes('value')).toBe('{"foo":"bar"}')
670+
expect(spy).toHaveBeenCalledTimes(1)
671+
672+
spy.mockRestore()
668673
})
669674
})
670675
})

packages/0/src/components/Popover/PopoverRoot.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
toggle: () => void
3838
}
3939
40-
export const [usePopoverContext, providePopoverContext] = createContext<PopoverContext>('Popover')
40+
export const [usePopoverContext, providePopoverContext] = createContext<PopoverContext>('v0:popover')
4141
</script>
4242

4343
<script setup lang="ts">

packages/0/src/components/Snackbar/index.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,8 @@ describe('snackbar', () => {
388388

389389
describe('integration', () => {
390390
it('should render Portal > Queue > Root > Content + Close', () => {
391+
const spy = vi.spyOn(console, 'warn').mockImplementation(() => {})
392+
391393
const [, provide, context] = createNotificationsContext()
392394
context.send({ subject: 'File uploaded', id: 'upload-1' })
393395

@@ -418,6 +420,9 @@ describe('snackbar', () => {
418420
expect(wrapper.findComponent(Snackbar.Root as any).exists()).toBe(true)
419421
expect(wrapper.findComponent(Snackbar.Content as any).text()).toBe('File uploaded')
420422
expect(wrapper.findComponent(Snackbar.Close as any).attributes('aria-label')).toBe('Close')
423+
expect(spy).toHaveBeenCalledTimes(1)
424+
425+
spy.mockRestore()
421426
})
422427
})
423428
})

0 commit comments

Comments
 (0)