Skip to content

Commit 5a768e2

Browse files
committed
test: add comprehensive tests for variants, sizes, and states
1 parent 30afa13 commit 5a768e2

File tree

3 files changed

+253
-3
lines changed

3 files changed

+253
-3
lines changed

packages/ui/src/components/button/Button.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ const cssVars = computed(() => {
6565
6666
const handleClick = (event: MouseEvent) => {
6767
emit('click', event)
68-
if (props.href) {
68+
if (props.href !== undefined && props.href !== null) {
6969
window.open(props.href, props.target)
7070
}
7171
}
Lines changed: 251 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
1+
import { describe, expect, it, vi } from 'vitest'
22
import { Button } from '@ant-design-vue/ui'
33
import { mount } from '@vue/test-utils'
44

@@ -7,4 +7,254 @@ describe('Button', () => {
77
const wrapper = mount(Button)
88
expect(wrapper.html()).toMatchSnapshot()
99
})
10+
11+
describe('Props', () => {
12+
it('should render different variants', () => {
13+
const variants = ['solid', 'outlined', 'text', 'link', 'dashed', 'filled'] as const
14+
15+
variants.forEach(variant => {
16+
const wrapper = mount(Button, {
17+
props: { variant },
18+
})
19+
expect(wrapper.classes()).toContain(`ant-btn-${variant}`)
20+
})
21+
})
22+
23+
it('should render different sizes', () => {
24+
const sizes = ['sm', 'md', 'lg'] as const
25+
26+
sizes.forEach(size => {
27+
const wrapper = mount(Button, {
28+
props: { size },
29+
})
30+
expect(wrapper.classes()).toContain(`ant-btn-${size}`)
31+
})
32+
})
33+
34+
it('should render loading state', () => {
35+
const wrapper = mount(Button, {
36+
props: { loading: true },
37+
})
38+
expect(wrapper.classes()).toContain('ant-btn-loading')
39+
expect(wrapper.findComponent({ name: 'LoadingOutlined' }).exists()).toBe(true)
40+
})
41+
42+
it('should render disabled state', () => {
43+
const wrapper = mount(Button, {
44+
props: { disabled: true },
45+
})
46+
expect(wrapper.classes()).toContain('ant-btn-disabled')
47+
expect(wrapper.attributes('disabled')).toBeDefined()
48+
})
49+
50+
it('should render danger state', () => {
51+
const wrapper = mount(Button, {
52+
props: { danger: true },
53+
})
54+
expect(wrapper.classes()).toContain('ant-btn-danger')
55+
expect(wrapper.classes()).toContain('ant-btn-custom-color')
56+
})
57+
58+
it('should render with custom color', () => {
59+
const wrapper = mount(Button, {
60+
props: { color: '#ff0000' },
61+
})
62+
expect(wrapper.classes()).toContain('ant-btn-custom-color')
63+
expect(wrapper.attributes('style')).toBeDefined()
64+
})
65+
66+
it('should handle href and target props', () => {
67+
const wrapper = mount(Button, {
68+
props: { href: 'https://example.com', target: '_blank' },
69+
})
70+
71+
// Mock window.open
72+
const mockOpen = vi.fn()
73+
vi.stubGlobal('open', mockOpen)
74+
75+
wrapper.trigger('click')
76+
expect(mockOpen).toHaveBeenCalledWith('https://example.com', '_blank')
77+
78+
vi.unstubAllGlobals()
79+
})
80+
})
81+
82+
describe('Events', () => {
83+
it('should emit click event', async () => {
84+
const wrapper = mount(Button)
85+
86+
await wrapper.trigger('click')
87+
88+
expect(wrapper.emitted('click')).toBeTruthy()
89+
expect(wrapper.emitted('click')?.[0]).toHaveLength(1)
90+
expect(wrapper.emitted('click')?.[0][0]).toBeInstanceOf(MouseEvent)
91+
})
92+
93+
it('should not emit click when disabled', async () => {
94+
const wrapper = mount(Button, {
95+
props: { disabled: true },
96+
})
97+
98+
await wrapper.trigger('click')
99+
100+
expect(wrapper.emitted('click')).toBeFalsy()
101+
})
102+
103+
it('should open link when href is provided', () => {
104+
const mockOpen = vi.fn()
105+
vi.stubGlobal('open', mockOpen)
106+
107+
const wrapper = mount(Button, {
108+
props: { href: 'https://example.com' },
109+
})
110+
111+
wrapper.trigger('click')
112+
expect(mockOpen).toHaveBeenCalledWith('https://example.com', undefined)
113+
114+
vi.unstubAllGlobals()
115+
})
116+
})
117+
118+
describe('Slots', () => {
119+
it('should render default slot content', () => {
120+
const wrapper = mount(Button, {
121+
slots: {
122+
default: 'Click Me',
123+
},
124+
})
125+
126+
expect(wrapper.text()).toContain('Click Me')
127+
})
128+
129+
it('should render icon slot', () => {
130+
const wrapper = mount(Button, {
131+
slots: {
132+
icon: '<i class="custom-icon"></i>',
133+
},
134+
})
135+
136+
expect(wrapper.find('.custom-icon').exists()).toBe(true)
137+
})
138+
139+
it('should render custom loading slot', () => {
140+
const wrapper = mount(Button, {
141+
props: { loading: true },
142+
slots: {
143+
loading: '<span class="custom-loading">Loading...</span>',
144+
},
145+
})
146+
147+
expect(wrapper.find('.custom-loading').exists()).toBe(true)
148+
expect(wrapper.text()).toContain('Loading...')
149+
})
150+
})
151+
152+
describe('Styles and Classes', () => {
153+
it('should apply base classes', () => {
154+
const wrapper = mount(Button)
155+
156+
expect(wrapper.classes()).toContain('ant-btn')
157+
expect(wrapper.classes()).toContain('ant-btn-solid') // default variant
158+
expect(wrapper.classes()).toContain('ant-btn-md') // default size
159+
})
160+
161+
it('should apply multiple state classes', () => {
162+
const wrapper = mount(Button, {
163+
props: {
164+
loading: true,
165+
danger: true,
166+
disabled: true,
167+
},
168+
})
169+
170+
expect(wrapper.classes()).toContain('ant-btn-loading')
171+
expect(wrapper.classes()).toContain('ant-btn-danger')
172+
expect(wrapper.classes()).toContain('ant-btn-disabled')
173+
expect(wrapper.classes()).toContain('ant-btn-custom-color')
174+
})
175+
176+
it('should apply custom color CSS variables', () => {
177+
const wrapper = mount(Button, {
178+
props: { color: '#1890ff' },
179+
})
180+
181+
const style = wrapper.attributes('style')
182+
expect(style).toBeDefined()
183+
})
184+
})
185+
186+
describe('Edge Cases', () => {
187+
it('should handle empty href', () => {
188+
const mockOpen = vi.fn()
189+
vi.stubGlobal('open', mockOpen)
190+
191+
const wrapper = mount(Button, {
192+
props: { href: '' },
193+
})
194+
195+
wrapper.trigger('click')
196+
expect(mockOpen).toHaveBeenCalledWith('', undefined)
197+
198+
vi.unstubAllGlobals()
199+
})
200+
201+
it('should handle loading and disabled states together', () => {
202+
const wrapper = mount(Button, {
203+
props: {
204+
loading: true,
205+
disabled: true,
206+
},
207+
})
208+
209+
expect(wrapper.classes()).toContain('ant-btn-loading')
210+
expect(wrapper.classes()).toContain('ant-btn-disabled')
211+
expect(wrapper.attributes('disabled')).toBeDefined()
212+
})
213+
214+
it('should render with all props combined', () => {
215+
const wrapper = mount(Button, {
216+
props: {
217+
variant: 'outlined',
218+
size: 'lg',
219+
loading: true,
220+
danger: true,
221+
color: '#ff4d4f',
222+
href: 'https://example.com',
223+
target: '_blank',
224+
},
225+
slots: {
226+
default: 'Complex Button',
227+
icon: '<i class="icon"></i>',
228+
},
229+
})
230+
231+
expect(wrapper.classes()).toContain('ant-btn')
232+
expect(wrapper.classes()).toContain('ant-btn-outlined')
233+
expect(wrapper.classes()).toContain('ant-btn-lg')
234+
expect(wrapper.classes()).toContain('ant-btn-loading')
235+
expect(wrapper.classes()).toContain('ant-btn-danger')
236+
expect(wrapper.classes()).toContain('ant-btn-custom-color')
237+
expect(wrapper.text()).toContain('Complex Button')
238+
expect(wrapper.find('.icon').exists()).toBe(true)
239+
})
240+
})
241+
242+
describe('Accessibility', () => {
243+
it('should have correct button element', () => {
244+
const wrapper = mount(Button)
245+
expect(wrapper.element.tagName).toBe('BUTTON')
246+
})
247+
248+
it('should handle disabled attribute correctly', () => {
249+
const enabledWrapper = mount(Button, {
250+
props: { disabled: false },
251+
})
252+
expect(enabledWrapper.attributes('disabled')).toBeUndefined()
253+
254+
const disabledWrapper = mount(Button, {
255+
props: { disabled: true },
256+
})
257+
expect(disabledWrapper.attributes('disabled')).toBeDefined()
258+
})
259+
})
10260
})

packages/ui/src/components/button/meta.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,4 @@ export const buttonSlots = {
107107
loading: (_: any) => null as any,
108108
} as const
109109

110-
export type ButtonSlots = typeof buttonSlots
110+
export type ButtonSlots = Partial<typeof buttonSlots>

0 commit comments

Comments
 (0)