fix: unit tests

This commit is contained in:
Yourz
2026-02-21 11:01:06 +08:00
parent d75fd0671d
commit b6f0fd57c2
5 changed files with 172 additions and 45 deletions

View File

@@ -0,0 +1,22 @@
import { mount } from '@vue/test-utils'
import { describe, expect, it } from 'vitest'
import MarqueeLine from './MarqueeLine.vue'
describe(MarqueeLine, () => {
it('renders slot content', () => {
const wrapper = mount(MarqueeLine, {
slots: { default: 'Hello World' }
})
expect(wrapper.text()).toBe('Hello World')
})
it('renders content inside a span within the container', () => {
const wrapper = mount(MarqueeLine, {
slots: { default: 'Test Text' }
})
const span = wrapper.find('span')
expect(span.exists()).toBe(true)
expect(span.text()).toBe('Test Text')
})
})

View File

@@ -1,58 +1,105 @@
import { describe, expect, it } from 'vitest'
import { mount } from '@vue/test-utils'
import { nextTick } from 'vue'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { splitTextAtWordBoundary } from './textTickerUtils'
import MarqueeLine from './MarqueeLine.vue'
import TextTickerMultiLine from './TextTickerMultiLine.vue'
describe('splitTextAtWordBoundary', () => {
it('returns full text when ratio >= 1 (fits in one line)', () => {
expect(splitTextAtWordBoundary('Load Checkpoint', 1)).toEqual([
'Load Checkpoint',
''
])
expect(splitTextAtWordBoundary('Load Checkpoint', 1.5)).toEqual([
'Load Checkpoint',
''
])
type Callback = () => void
const resizeCallbacks: Callback[] = []
const mutationCallbacks: Callback[] = []
vi.mock('@vueuse/core', async () => {
const actual = await vi.importActual('@vueuse/core')
return {
...actual,
useResizeObserver: (_target: unknown, cb: Callback) => {
resizeCallbacks.push(cb)
return { stop: vi.fn() }
},
useMutationObserver: (_target: unknown, cb: Callback) => {
mutationCallbacks.push(cb)
return { stop: vi.fn() }
}
}
})
function mockElementSize(
el: HTMLElement,
clientWidth: number,
scrollWidth: number
) {
Object.defineProperty(el, 'clientWidth', {
value: clientWidth,
configurable: true
})
Object.defineProperty(el, 'scrollWidth', {
value: scrollWidth,
configurable: true
})
}
describe(TextTickerMultiLine, () => {
let wrapper: ReturnType<typeof mount>
afterEach(() => {
wrapper?.unmount()
resizeCallbacks.length = 0
mutationCallbacks.length = 0
})
it('splits at last word boundary before estimated break', () => {
// "Load Checkpoint Loader" = 22 chars, ratio 0.5 → estimate at char 11
// lastIndexOf(' ', 11) → 15? No: "Load Checkpoint Loader"
// 0123456789...
// ' ' at index 4 and 15
// lastIndexOf(' ', 11) → 4
expect(splitTextAtWordBoundary('Load Checkpoint Loader', 0.5)).toEqual([
'Load',
'Checkpoint Loader'
])
function mountComponent(text: string) {
wrapper = mount(TextTickerMultiLine, {
slots: { default: text }
})
return wrapper
}
function getMeasureEl(): HTMLElement {
return wrapper.find('[aria-hidden="true"]').element as HTMLElement
}
async function triggerSplitLines() {
resizeCallbacks.forEach((cb) => cb())
await nextTick()
}
it('renders slot content', () => {
mountComponent('Load Checkpoint')
expect(wrapper.text()).toContain('Load Checkpoint')
})
it('splits longer text proportionally', () => {
// ratio 0.7 → estimate at char 15
// lastIndexOf(' ', 15) → 15 (the space between "Checkpoint" and "Loader")
expect(splitTextAtWordBoundary('Load Checkpoint Loader', 0.7)).toEqual([
'Load Checkpoint',
'Loader'
])
it('renders a single MarqueeLine when text fits', async () => {
mountComponent('Short')
mockElementSize(getMeasureEl(), 200, 100)
await triggerSplitLines()
expect(wrapper.findAllComponents(MarqueeLine)).toHaveLength(1)
})
it('returns full text when no word boundary found', () => {
expect(splitTextAtWordBoundary('Superlongwordwithoutspaces', 0.5)).toEqual([
'Superlongwordwithoutspaces',
''
])
it('renders two MarqueeLines when text overflows', async () => {
mountComponent('Load Checkpoint Loader Simple')
mockElementSize(getMeasureEl(), 100, 300)
await triggerSplitLines()
expect(wrapper.findAllComponents(MarqueeLine)).toHaveLength(2)
})
it('handles single word text', () => {
expect(splitTextAtWordBoundary('Checkpoint', 0.5)).toEqual([
'Checkpoint',
''
])
it('splits text at word boundary when overflowing', async () => {
mountComponent('Load Checkpoint Loader')
mockElementSize(getMeasureEl(), 100, 200)
await triggerSplitLines()
const lines = wrapper.findAllComponents(MarqueeLine)
expect(lines[0].text()).toBe('Load')
expect(lines[1].text()).toBe('Checkpoint Loader')
})
it('handles ratio near zero', () => {
expect(splitTextAtWordBoundary('Load Checkpoint Loader', 0.1)).toEqual([
'Load Checkpoint Loader',
''
])
it('has hidden measurement element with aria-hidden', () => {
mountComponent('Test')
const measureEl = wrapper.find('[aria-hidden="true"]')
expect(measureEl.exists()).toBe(true)
expect(measureEl.classes()).toContain('invisible')
})
})

View File

@@ -25,7 +25,7 @@ import { useMutationObserver, useResizeObserver } from '@vueuse/core'
import { ref } from 'vue'
import MarqueeLine from './MarqueeLine.vue'
import { splitTextAtWordBoundary } from './textTickerUtils'
import { splitTextAtWordBoundary } from '@/utils/textTickerUtils'
const measureRef = ref<HTMLElement | null>(null)
const firstLine = ref('')

View File

@@ -0,0 +1,58 @@
import { describe, expect, it } from 'vitest'
import { splitTextAtWordBoundary } from '@/utils/textTickerUtils'
describe('splitTextAtWordBoundary', () => {
it('returns full text when ratio >= 1 (fits in one line)', () => {
expect(splitTextAtWordBoundary('Load Checkpoint', 1)).toEqual([
'Load Checkpoint',
''
])
expect(splitTextAtWordBoundary('Load Checkpoint', 1.5)).toEqual([
'Load Checkpoint',
''
])
})
it('splits at last word boundary before estimated break', () => {
// "Load Checkpoint Loader" = 22 chars, ratio 0.5 → estimate at char 11
// lastIndexOf(' ', 11) → 15? No: "Load Checkpoint Loader"
// 0123456789...
// ' ' at index 4 and 15
// lastIndexOf(' ', 11) → 4
expect(splitTextAtWordBoundary('Load Checkpoint Loader', 0.5)).toEqual([
'Load',
'Checkpoint Loader'
])
})
it('splits longer text proportionally', () => {
// ratio 0.7 → estimate at char 15
// lastIndexOf(' ', 15) → 15 (the space between "Checkpoint" and "Loader")
expect(splitTextAtWordBoundary('Load Checkpoint Loader', 0.7)).toEqual([
'Load Checkpoint',
'Loader'
])
})
it('returns full text when no word boundary found', () => {
expect(splitTextAtWordBoundary('Superlongwordwithoutspaces', 0.5)).toEqual([
'Superlongwordwithoutspaces',
''
])
})
it('handles single word text', () => {
expect(splitTextAtWordBoundary('Checkpoint', 0.5)).toEqual([
'Checkpoint',
''
])
})
it('handles ratio near zero', () => {
expect(splitTextAtWordBoundary('Load Checkpoint Loader', 0.1)).toEqual([
'Load Checkpoint Loader',
''
])
})
})