Skip to content

Commit 1a3341c

Browse files
committed
test(collapse-transition): add useCollapseTransition unit coverage
Cover the composable's raf/doubleRaf branches, element-removal paths and init-trigger handling against the current source.
1 parent e04e888 commit 1a3341c

1 file changed

Lines changed: 189 additions & 2 deletions

File tree

  • packages/varlet-ui/src/collapse-transition/__tests__

packages/varlet-ui/src/collapse-transition/__tests__/index.spec.js

Lines changed: 189 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
import * as shared from '@varlet/shared'
12
import { mount } from '@vue/test-utils'
2-
import { describe, expect, test } from 'vite-plus/test'
3-
import { createApp, ref } from 'vue'
3+
import { describe, expect, test, vi } from 'vite-plus/test'
4+
import { createApp, nextTick, ref } from 'vue'
45
import CollapseTransition from '..'
56
import { delay } from '../../utils/test'
67
import VarCollapseTransition from '../CollapseTransition'
8+
import { useCollapseTransition } from '../useCollapseTransition'
79

810
describe('test collapseTransition render', () => {
911
test('collapseTransition use', () => {
@@ -56,3 +58,188 @@ describe('test collapseTransition props', () => {
5658
wrapper.unmount()
5759
})
5860
})
61+
62+
describe('useCollapseTransition behaviors', () => {
63+
test('useCollapseTransition returns early when contentEl is missing', async () => {
64+
const contentEl = ref(null)
65+
const showContent = ref(false)
66+
const expand = ref(true)
67+
68+
useCollapseTransition({ contentEl, showContent, expand })
69+
70+
await nextTick()
71+
await Promise.resolve()
72+
73+
expect(showContent.value).toBe(false)
74+
})
75+
76+
test('useCollapseTransition skips init trigger after transition start', async () => {
77+
const rafSpy = vi.spyOn(shared, 'raf').mockResolvedValue()
78+
const doubleRafSpy = vi.spyOn(shared, 'doubleRaf').mockResolvedValue()
79+
80+
const element = document.createElement('div')
81+
Object.defineProperty(element, 'offsetHeight', { value: 24, configurable: true })
82+
83+
const contentEl = ref(element)
84+
const showContent = ref(false)
85+
const expand = ref(false)
86+
87+
const { handleTransitionStart } = useCollapseTransition({ contentEl, showContent, expand })
88+
89+
handleTransitionStart()
90+
expand.value = true
91+
await nextTick()
92+
await Promise.resolve()
93+
94+
expect(showContent.value).toBe(true)
95+
96+
rafSpy.mockRestore()
97+
doubleRafSpy.mockRestore()
98+
})
99+
100+
test('useCollapseTransition handles transition end when still expanding', () => {
101+
const element = document.createElement('div')
102+
const contentEl = ref(element)
103+
const showContent = ref(true)
104+
const expand = ref(true)
105+
106+
const { handleTransitionEnd } = useCollapseTransition({ contentEl, showContent, expand })
107+
108+
handleTransitionEnd()
109+
expect(showContent.value).toBe(true)
110+
})
111+
112+
test('useCollapseTransition triggers init transition end', async () => {
113+
const rafSpy = vi.spyOn(shared, 'raf').mockResolvedValue()
114+
const doubleRafSpy = vi.spyOn(shared, 'doubleRaf').mockResolvedValue()
115+
const element = document.createElement('div')
116+
Object.defineProperty(element, 'offsetHeight', { value: 24, configurable: true })
117+
118+
const contentEl = ref(element)
119+
const showContent = ref(false)
120+
const expand = ref(true)
121+
122+
useCollapseTransition({ contentEl, showContent, expand })
123+
124+
await nextTick()
125+
for (let i = 0; i < 6; i += 1) {
126+
if (contentEl.value?.style.height === '') {
127+
break
128+
}
129+
await Promise.resolve()
130+
}
131+
132+
expect(showContent.value).toBe(true)
133+
expect(contentEl.value?.style.height).toBe('')
134+
135+
rafSpy.mockRestore()
136+
doubleRafSpy.mockRestore()
137+
})
138+
139+
test('useCollapseTransition close returns early when contentEl is missing', async () => {
140+
const contentEl = ref(null)
141+
const showContent = ref(false)
142+
const expand = ref(false)
143+
144+
useCollapseTransition({ contentEl, showContent, expand })
145+
146+
await nextTick()
147+
await Promise.resolve()
148+
149+
expect(showContent.value).toBe(false)
150+
})
151+
152+
test('useCollapseTransition ignores transition events from other elements or properties', () => {
153+
const element = document.createElement('div')
154+
const contentEl = ref(element)
155+
const showContent = ref(true)
156+
const expand = ref(false)
157+
158+
const { handleTransitionEnd, handleTransitionStart } = useCollapseTransition({ contentEl, showContent, expand })
159+
160+
// Event from a different target is ignored (useCollapseTransition.ts lines 85, 101)
161+
const otherElement = document.createElement('div')
162+
const otherTargetEvent = new Event('transitionend')
163+
Object.defineProperty(otherTargetEvent, 'target', { value: otherElement })
164+
Object.assign(otherTargetEvent, { propertyName: 'height' })
165+
handleTransitionEnd(otherTargetEvent)
166+
167+
// Event for a non-height property is ignored (useCollapseTransition.ts lines 85, 101)
168+
const otherPropEvent = new Event('transitionend')
169+
Object.defineProperty(otherPropEvent, 'target', { value: element })
170+
Object.assign(otherPropEvent, { propertyName: 'width' })
171+
handleTransitionEnd(otherPropEvent)
172+
173+
const otherStartEvent = new Event('transitionstart')
174+
Object.defineProperty(otherStartEvent, 'target', { value: otherElement })
175+
Object.assign(otherStartEvent, { propertyName: 'height' })
176+
handleTransitionStart(otherStartEvent)
177+
178+
// Since all events were ignored, the showContent collapse logic never ran
179+
expect(showContent.value).toBe(true)
180+
})
181+
182+
test('useCollapseTransition ignores transition end from a stale version', async () => {
183+
const element = document.createElement('div')
184+
Object.defineProperty(element, 'offsetHeight', { value: 24, configurable: true })
185+
186+
const contentEl = ref(element)
187+
const showContent = ref(true)
188+
const expand = ref(false)
189+
190+
const { handleTransitionEnd, handleTransitionStart } = useCollapseTransition({ contentEl, showContent, expand })
191+
192+
// startedTransitionVersion is captured here from the current transitionVersion
193+
handleTransitionStart()
194+
195+
// Bumping expand increments transitionVersion, so the started version is now stale
196+
expand.value = true
197+
await nextTick()
198+
199+
// Event-driven end with a stale startedTransitionVersion returns early
200+
// (useCollapseTransition.ts lines 89-90); showContent is not flipped to false
201+
const endEvent = new Event('transitionend')
202+
Object.defineProperty(endEvent, 'target', { value: element })
203+
Object.assign(endEvent, { propertyName: 'height' })
204+
handleTransitionEnd(endEvent)
205+
206+
expect(showContent.value).toBe(true)
207+
})
208+
209+
test('useCollapseTransition skips handleTransitionEnd when isInitToTrigger is false', async () => {
210+
const rafSpy = vi.spyOn(shared, 'raf').mockResolvedValue()
211+
212+
let resolveDoubleRaf
213+
const doubleRafSpy = vi.spyOn(shared, 'doubleRaf').mockImplementation(
214+
() =>
215+
new Promise((resolve) => {
216+
resolveDoubleRaf = resolve
217+
}),
218+
)
219+
220+
const element = document.createElement('div')
221+
Object.defineProperty(element, 'offsetHeight', { value: 24, configurable: true })
222+
223+
const contentEl = ref(element)
224+
const showContent = ref(false)
225+
const expand = ref(true)
226+
227+
const { handleTransitionStart } = useCollapseTransition({ contentEl, showContent, expand })
228+
229+
await nextTick()
230+
await Promise.resolve()
231+
await Promise.resolve()
232+
await Promise.resolve()
233+
234+
handleTransitionStart()
235+
resolveDoubleRaf?.()
236+
await Promise.resolve()
237+
await Promise.resolve()
238+
239+
expect(showContent.value).toBe(true)
240+
expect(contentEl.value?.style.height).toBe('24px')
241+
242+
rafSpy.mockRestore()
243+
doubleRafSpy.mockRestore()
244+
})
245+
})

0 commit comments

Comments
 (0)