-
Notifications
You must be signed in to change notification settings - Fork 163
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test(Image): improve unit test coverage
- Loading branch information
Showing
3 changed files
with
170 additions
and
63 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// Vitest Snapshot v1 | ||
|
||
exports[`Image > props > : lazy 1`] = ` | ||
<div | ||
class="t-image t-image--round" | ||
> | ||
<!--v-if--> | ||
<img | ||
alt="" | ||
class="t-image__img" | ||
src="https://tdesign.gtimg.com/site/upload1.png" | ||
style="object-fit: fill; object-position: center;" | ||
/> | ||
</div> | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,86 +1,144 @@ | ||
import { mount } from '@vue/test-utils'; | ||
import { describe, it, expect } from 'vitest'; | ||
import Image from '../image.vue'; | ||
import { vi, describe, it, expect } from 'vitest'; | ||
import { LoadingIcon } from 'tdesign-icons-vue-next'; | ||
import { nextTick } from 'vue'; | ||
import { vi } from 'vitest'; | ||
import Image from '../image.vue'; | ||
import { MockIntersectionObserver } from './utils'; | ||
|
||
const prefix = 't' | ||
const name = `${prefix}-image`; | ||
const IMAGE = 'https://tdesign.gtimg.com/site/upload1.png'; | ||
const FAIL_IMAGE = 'https://123.jpg'; | ||
const fitList = ['fill', 'contain', 'cover', 'none', 'scale-down']; | ||
const shapeList = ['circle', 'round', 'square']; | ||
const positionList = ['top', 'bottom', 'center', 'left', 'right']; | ||
|
||
describe('Image.vue', () => { | ||
it('create', async () => { | ||
const wrapper = mount(() => <Image src={IMAGE} />); | ||
const img = wrapper.find('.t-image__img'); | ||
expect(wrapper.classes()).toContain('t-image'); | ||
expect(img.exists()).toBeTruthy(); | ||
expect(img.attributes('src')).toBe(IMAGE); | ||
describe('Image', () => { | ||
beforeAll(() => { | ||
window.IntersectionObserver = MockIntersectionObserver; | ||
}); | ||
|
||
it('alt render', async () => { | ||
const wrapper = mount(() => <Image src={IMAGE} alt="图片" />); | ||
const img = wrapper.find('.t-image__img'); | ||
expect(wrapper.classes()).toContain('t-image'); | ||
expect(img.attributes('alt')).toBe('图片'); | ||
}); | ||
describe('props', () => { | ||
it(': lazy', async () => { | ||
const wrapper = mount(() => <Image src={IMAGE} lazy />); | ||
await nextTick(); | ||
const $image = wrapper.find(`.${name}__img`); | ||
expect(wrapper.find(`.${name}__status`).exists()).toBeTruthy(); | ||
// 触发 IntersectionObserver , 但图片加载完成不会触发 Load 回调, | ||
$image.trigger('resize'); | ||
await nextTick(); | ||
expect($image.attributes('src')).toBe(IMAGE); | ||
// 手动触发 图片加载完成的回调函数 | ||
await $image.trigger('Load'); | ||
expect(wrapper.element).toMatchSnapshot(); | ||
expect(wrapper.find(`.${name}__status`).exists()).toBeFalsy(); | ||
}); | ||
|
||
it('fit render', async () => { | ||
fitList.forEach((fit) => { | ||
const wrapper = mount(() => <Image src={IMAGE} fit={fit} />); | ||
const img = wrapper.find('.t-image__img'); | ||
expect(img.attributes('style')).toContain(`object-fit: ${fit}`); | ||
it(': alt', async () => { | ||
const wrapper = mount(() => <Image src={IMAGE} alt="图片" />); | ||
const $image = wrapper.find(`.${name}__img`); | ||
expect(wrapper.classes()).toContain(`${name}`); | ||
expect($image.attributes('alt')).toBe('图片'); | ||
}); | ||
}); | ||
|
||
it('shape render', async () => { | ||
shapeList.forEach((shape) => { | ||
const wrapper = mount(() => <Image src={IMAGE} shape={shape} />); | ||
expect(wrapper.classes()).toContain(`t-image--${shape}`); | ||
it(': fit', async () => { | ||
const wrapper = mount(Image, { | ||
props: { | ||
fit: '', | ||
src: IMAGE, | ||
} | ||
}) | ||
const $image = wrapper.find(`.${name}__img`); | ||
// fit = '' | ||
fitList.forEach(item => { | ||
expect($image.attributes('style').includes(`object-fit: ${item}`)).toBeFalsy(); | ||
}) | ||
|
||
const fit = 'cover'; | ||
await wrapper.setProps({ | ||
fit, | ||
}); | ||
// fit = 'square' | ||
expect($image.attributes('style')).toContain(`object-fit: ${fit}`); | ||
}); | ||
}); | ||
|
||
it('position render', async () => { | ||
positionList.forEach((position) => { | ||
const wrapper = mount(() => <Image src={IMAGE} position={position} />); | ||
const img = wrapper.find('.t-image__img'); | ||
expect(img.attributes('style')).toContain(`object-position: ${position}`); | ||
it(': shape', async () => { | ||
const wrapper = mount(Image, { | ||
props: { | ||
shape: '', | ||
src: IMAGE, | ||
} | ||
}) | ||
const $image = wrapper.findComponent(Image); | ||
|
||
// shape = '' | ||
shapeList.forEach(item => { | ||
expect($image.classes().includes(`${name}--${item}`)).toBeFalsy(); | ||
}) | ||
|
||
const shape = 'square'; | ||
await wrapper.setProps({ | ||
shape, | ||
}); | ||
// shape = 'square' | ||
expect($image.classes()).toContain(`${name}--${shape}`); | ||
}); | ||
}); | ||
|
||
it('loading render', async () => { | ||
const slots = { | ||
loading: () => <LoadingIcon />, | ||
}; | ||
const wrapper = mount(() => <Image src={IMAGE} v-slots={slots} />); | ||
const status = wrapper.find('.t-image__status'); | ||
expect(status.exists()).toBeTruthy(); | ||
expect(wrapper.findComponent(LoadingIcon).exists()).toBeTruthy(); | ||
}); | ||
it(': position', async () => { | ||
positionList.forEach((position) => { | ||
const wrapper = mount(() => <Image src={IMAGE} position={position} />); | ||
const $image = wrapper.find('.t-image__img'); | ||
expect($image.attributes('style')).toContain(`object-position: ${position}`); | ||
}); | ||
}); | ||
|
||
it('error render', async () => { | ||
const onError = vi.fn(); | ||
const slots = { | ||
error: () => '加载失败', | ||
}; | ||
const wrapper = mount(() => <Image src={FAIL_IMAGE} v-slots={slots} onError={onError} />); | ||
const img = wrapper.find('.t-image__img'); | ||
const status = wrapper.find('.t-image__status'); | ||
await nextTick(); | ||
await img.trigger('error'); | ||
expect(status.exists()).toBeTruthy(); | ||
// expect(status.text()).toBe('加载失败'); | ||
// expect(onError).toBeCalled(); | ||
}); | ||
it(': loading', async () => { | ||
const slots = { | ||
loading: () => <LoadingIcon />, | ||
}; | ||
const wrapper = mount(() => <Image src={IMAGE} v-slots={slots} />); | ||
const $status = wrapper.find(`.${name}__status`); | ||
expect($status.exists()).toBeTruthy(); | ||
expect(wrapper.findComponent(LoadingIcon).exists()).toBeTruthy(); | ||
}); | ||
|
||
it('load render', async () => { | ||
const onLoad = vi.fn(); | ||
const wrapper = mount(() => <Image src={IMAGE} onLoad={onLoad} />); | ||
const img = wrapper.find('.t-image__img'); | ||
await nextTick(); | ||
img.trigger('load'); | ||
// expect(onLoad).toBeCalled(); | ||
it(': src', async () => { | ||
const onError = vi.fn(); | ||
const wrapper = mount(() => <Image src='' onError={onError} />); | ||
await nextTick(); | ||
const $image = wrapper.find(`.${name}__img`); | ||
// 手动触发 图片加载失败的回调函数 | ||
await $image.trigger('Error'); | ||
expect(wrapper.find(`.${name}__status`).exists()).toBeTruthy(); | ||
// src = '',不会触发 error | ||
expect(onError).toBeCalledTimes(0); | ||
}); | ||
|
||
it(': onError', async () => { | ||
const onError = vi.fn(); | ||
const slots = { | ||
error: () => '加载失败', | ||
}; | ||
const wrapper = mount(() => <Image src={FAIL_IMAGE} v-slots={slots} onError={onError} />); | ||
await nextTick(); | ||
const $image = wrapper.find(`.${name}__img`); | ||
// 手动触发 图片加载失败的回调函数 | ||
await $image.trigger('Error'); | ||
const status = wrapper.find(`.${name}__status`); | ||
expect(status.exists()).toBeTruthy(); | ||
expect(status.text()).toBe('加载失败'); | ||
expect(onError).toBeCalledTimes(1); | ||
}); | ||
|
||
it(': onLoad', async () => { | ||
const onLoad = vi.fn(); | ||
const wrapper = mount(() => <Image src={IMAGE} onLoad={onLoad} />); | ||
await nextTick(); | ||
const $image = wrapper.find(`.${name}__img`); | ||
expect($image.attributes('src')).toBe(IMAGE); | ||
// 手动触发 图片加载完成的回调函数 | ||
await $image.trigger('Load'); | ||
expect(onLoad).toBeCalledTimes(1); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
interface IntersectionObserverCallback { | ||
(entries: IntersectionObserverEntry[], observer: IntersectionObserver): void; | ||
} | ||
|
||
export class MockIntersectionObserver { | ||
_callback: Function; | ||
|
||
_element!: HTMLElement; | ||
|
||
constructor(callback: Function) { | ||
this._callback = callback; | ||
} | ||
|
||
observe(element: HTMLElement) { | ||
this._element = element; | ||
this._element.addEventListener('resize', this.trigger); | ||
} | ||
|
||
unobserve() { | ||
this._element.removeEventListener('resize', this.trigger); | ||
} | ||
|
||
disconnect() { | ||
this._element.removeEventListener('resize', this.trigger); | ||
} | ||
|
||
trigger = (event: UIEvent) => { | ||
this._callback([ | ||
{ | ||
isIntersecting: true, | ||
}, | ||
]); | ||
}; | ||
} |