Skip to content

Commit

Permalink
Merge pull request #332 from palmcivet/test/image-viewer/unit
Browse files Browse the repository at this point in the history
test(image-viewer): add unit test for image-viewer
  • Loading branch information
LeeJim authored Sep 16, 2022
2 parents 8e19875 + f22ac06 commit a2d34f7
Show file tree
Hide file tree
Showing 7 changed files with 315 additions and 11 deletions.
109 changes: 109 additions & 0 deletions src/image-viewer/__test__/__snapshots__/index.test.jsx.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Vitest Snapshot v1

exports[`ImageViewer > function > : onIndexChange 1`] = `
<transition-stub
class="t-image-viewer"
>
<div
class="t-overlay t-overlay--active"
style="z-index: 1000; transition-duration: 300ms;"
>
<div
class="t-image-viewer__close-icon"
>
<svg
class="t-icon t-icon-close-circle-filled"
fill="none"
height="1em"
viewBox="0 0 16 16"
width="1em"
>
<path
d="M15 8A7 7 0 101 8a7 7 0 0014 0zM5.67 4.95L8 7.29l2.33-2.34.7.7L8.7 8l2.34 2.35-.71.7L8 8.71l-2.33 2.34-.7-.7L7.3 8 4.96 5.65l.71-.7z"
fill="currentColor"
fill-opacity="0.9"
/>
</svg>
</div>
<div
class="t-swiper t-image-viewer__swipe"
style="overflow: hidden;"
>
<div
class="t-swiper__container"
data-is-trust="true"
style="flex-direction: row; transform: translateX(-0px); transition: transform 300ms;"
>
<div
class="t-swiper-item t-image-viewer__swipe-item copy-item"
>
<img
class="t-image-viewer__image"
src="https://imgcache.qq.com/open_proj/proj_qcloud_v2/rocket_images/1606728019829_yw760ok1jmpbep14i.png"
style="transition-duration: .3s;"
/>
</div>
<div
class="t-swiper-item t-image-viewer__swipe-item"
>
<img
class="t-image-viewer__image"
src="https://imgcache.qq.com/open_proj/proj_qcloud_v2/rocket_images/1606728019829_yw760ok1jmpbep14i.png"
style="transition-duration: .3s;"
/>
</div>
<div
class="t-swiper-item t-image-viewer__swipe-item"
>
<img
class="t-image-viewer__image"
src="https://imgcache.qq.com/open_proj/proj_qcloud_v2/rocket_images/1606728019829_yw760ok1jmpbep14i.png"
style="transition-duration: .3s;"
/>
</div>
<div
class="t-swiper-item t-image-viewer__swipe-item copy-item"
>
<img
class="t-image-viewer__image"
src="https://imgcache.qq.com/open_proj/proj_qcloud_v2/rocket_images/1606728019829_yw760ok1jmpbep14i.png"
style="transition-duration: .3s;"
/>
</div>
</div>
<!--v-if-->
<span
class="t-swiper__pagination t-swiper__pagination-dots t-swiper__pagination-bottom"
>
<span
class="t-swiper-dot"
/>
<span
class="t-swiper-dot"
/>
<!--v-if-->
</span>
</div>
</div>
</transition-stub>
`;
120 changes: 120 additions & 0 deletions src/image-viewer/__test__/index.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { nextTick, ref } from 'vue';
import { mount } from '@vue/test-utils';
import { CloseCircleFilledIcon, AddCircleIcon } from 'tdesign-icons-vue-next';
import { describe, it, vi } from 'vitest';
import ImageViewer from '../image-viewer.vue';
import { Swiper } from '../../swiper';
import { trigger, triggerZoom } from './touch';

const images = ref([
'https://imgcache.qq.com/open_proj/proj_qcloud_v2/rocket_images/1606728019829_yw760ok1jmpbep14i.png',
'https://imgcache.qq.com/open_proj/proj_qcloud_v2/rocket_images/1606728019829_yw760ok1jmpbep14i.png',
'https://imgcache.qq.com/open_proj/proj_qcloud_v2/rocket_images/1606728019829_yw760ok1jmpbep14i.png',
]);

describe('ImageViewer', () => {
describe('props', () => {
it(': closeBtn', async () => {
const wrapper = mount(<ImageViewer v-model:images={images.value} visible={true} closeBtn={false} />);

expect(wrapper.findComponent(CloseCircleFilledIcon).exists()).toBe(false);
});

it(': images', async () => {
const emptyImages = mount(<ImageViewer visible={true} />);
expect(emptyImages.find('.t-swiper-item').exists()).toBe(false);

const wrapper = mount(<ImageViewer v-model:images={images.value} visible={true} />);

await nextTick();
expect(wrapper.findAll('.t-swiper-item').length - 2).toBe(images.value.length);

images.value.pop();
await nextTick();
expect(wrapper.findAll('.t-swiper-item').length - 2).toBe(images.value.length);
});

it(': maxZoom', async () => {
const wrapper = mount(<ImageViewer v-model:images={images.value} visible={true} maxZoom={2} />);

const target = wrapper.find('.t-swiper-item');
triggerZoom(target, 200, 200, 'in');
expect(wrapper.vm.imageStyle).toMatchObject({ transform: `scale(${2}, ${2})` });

triggerZoom(target, 200, 200, 'out');
expect(wrapper.vm.imageStyle).not.toHaveProperty('transform');

// 测试 toggleScale
trigger(target, 'touchstart', 50, 50);
trigger(target, 'touchend', 50, 50);

await new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, 100); // 小于 TAP_TIME
});
trigger(target, 'touchstart', 50, 50);
trigger(target, 'touchend', 50, 50);
});

it(': showIndex', async () => {
const wrapper = mount(<ImageViewer v-model:images={images.value} visible={true} showIndex={true} />);

expect(wrapper.find('.t-swiper__pagination-fraction').exists()).toBe(true);
});

it(': visible', async () => {
const visible = ref(false);
const wrapper = mount(<ImageViewer v-model:images={images.value} v-model:visible={visible.value} />);

expect(wrapper.findComponent(Swiper).exists()).toBe(false);
visible.value = true;
});
});

describe('slots', () => {
it(': closeBtn', () => {
const wrapper = mount(
<ImageViewer v-model:images={images.value} visible={true} closeBtn={() => <AddCircleIcon />} />,
);

expect(wrapper.findComponent(AddCircleIcon).exists()).toBe(true);
});
});

describe('event', () => {
it(': close', async () => {
const wrapper = mount(<ImageViewer v-model:images={images.value} visible={true} />);

await wrapper.findComponent(CloseCircleFilledIcon).trigger('click');
expect(wrapper.emitted()).toHaveProperty('close');
});
});

describe('function', () => {
it(': onClose', async () => {
const onClose = vi.fn();

const wrapper = mount(<ImageViewer v-model:images={images.value} visible={true} onClose={onClose} />);

await wrapper.findComponent(CloseCircleFilledIcon).trigger('click');
expect(onClose).toHaveBeenCalled();
});

it(': onIndexChange', async () => {
const onIndexChange = vi.fn();

const wrapper = mount(<ImageViewer v-model:images={images.value} visible={true} onIndexChange={onIndexChange} />);

const target = wrapper.find('.t-swiper__container');

await trigger(target, 'touchstart', 0, 0);
await trigger(target, 'touchmove', 30, 0);
await trigger(target, 'touchmove', 60, 0);
await trigger(target, 'touchmove', 120, 0);
await trigger(target, 'touchend', 120, 0);

expect(wrapper.element).toMatchSnapshot();
});
});
});
75 changes: 75 additions & 0 deletions src/image-viewer/__test__/touch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
* https://github.com/vant-ui/vant/blob/dev/packages/vant/test/event.ts
*/

import { ComponentPublicInstance, nextTick } from 'vue';
import { VueWrapper, DOMWrapper } from '@vue/test-utils';

function getTouch(el: Element | Window, x: number, y: number) {
return {
identifier: Date.now(),
target: el,
pageX: x,
pageY: y,
clientX: x,
clientY: y,
radiusX: 2.5,
radiusY: 2.5,
rotationAngle: 10,
force: 0.5,
};
}

// trigger pointer/touch event
export function trigger(
wrapper: VueWrapper<ComponentPublicInstance<any, any, any>> | DOMWrapper<Element> | Element | Window,
eventName: string,
x = 0,
y = 0,
options: any = {},
) {
const el = 'element' in wrapper ? wrapper.element : wrapper;
const touchList = options.touchList || [getTouch(el, x, y)];

if (options.x || options.y) {
touchList.push(getTouch(el, options.x, options.y));
}

const event = document.createEvent('CustomEvent');
event.initCustomEvent(eventName, true, true, {});

Object.assign(event, {
clientX: x,
clientY: y,
touches: touchList,
targetTouches: touchList,
changedTouches: touchList,
});

el.dispatchEvent(event);

return nextTick();
}

function triggerTwoFingerTouchmove(el: Element | DOMWrapper<Element>, x: number, y: number) {
trigger(el, 'touchmove', -x, -y, { x, y });
}

// simulate zoom
export function triggerZoom(el: Element | DOMWrapper<Element>, x: number, y: number, direction: 'in' | 'out' = 'in') {
trigger(el, 'touchstart', 0, 0, { x, y });

if (direction === 'in') {
triggerTwoFingerTouchmove(el, x / 4, y / 4);
triggerTwoFingerTouchmove(el, x / 3, y / 3);
triggerTwoFingerTouchmove(el, x / 2, y / 2);
triggerTwoFingerTouchmove(el, x, y);
} else if (direction === 'out') {
triggerTwoFingerTouchmove(el, x, y);
triggerTwoFingerTouchmove(el, x / 2, y / 2);
triggerTwoFingerTouchmove(el, x / 3, y / 3);
triggerTwoFingerTouchmove(el, x / 4, y / 4);
}

trigger(el, 'touchend', 0, 0, { touchList: [] });
}
1 change: 0 additions & 1 deletion src/image-viewer/demos/mobile.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
</template>
<script lang="ts">
import { ref, defineComponent, Ref } from 'vue';
import { TdImageViewerProps } from '../type';
export default defineComponent({
setup() {
Expand Down
11 changes: 6 additions & 5 deletions src/image-viewer/image-viewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ import {
defineComponent,
reactive,
watch,
nextTick,
PropType,
getCurrentInstance,
CSSProperties,
SetupContext,
Expand All @@ -46,7 +44,8 @@ import config from '../config';
import ImageViewerProps from './props';
import { renderTNode, TNode, useEmitEvent, useDefault, useTouch } from '../shared';
import { TdImageViewerProps } from './type';
import { Toast } from '..';
import { Swiper as TSwiper, SwiperItem as TSwiperItem, SwiperNavigation } from '../swiper';
import TOverlay from '../overlay';
export type TriggerType = 'close-btn' | 'overlay' | 'esc';
const { prefix } = config;
Expand All @@ -65,6 +64,9 @@ export default defineComponent({
name,
components: {
CloseCircleFilledIcon,
TSwiper,
TSwiperItem,
TOverlay,
TNode,
},
props: ImageViewerProps,
Expand All @@ -88,7 +90,7 @@ export default defineComponent({
const closeBtnTNode = computed(() => {
return renderTNode(internalInstance, 'closeBtn');
});
const navigation = computed(() => {
const navigation = computed<SwiperNavigation>(() => {
if (props.showIndex) {
return { type: 'fraction' };
}
Expand Down Expand Up @@ -126,7 +128,6 @@ export default defineComponent({
event.preventDefault();
event.stopPropagation();
const { touches } = event;
const { offsetX } = touch;
touch.start(event);
Expand Down
8 changes: 4 additions & 4 deletions src/swiper/swiper.vue
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ import {
ComponentPublicInstance,
} from 'vue';
import { ChevronLeftIcon, ChevronRightIcon } from 'tdesign-icons-vue-next';
import { SwipeDirection, useSwipe } from '@vueuse/core';
import { useSwipe } from '@vueuse/core';
import SwiperProps from './props';
import config from '../config';
import { renderTNode, useDefault, TNode } from '../shared';
Expand Down Expand Up @@ -109,16 +109,16 @@ export default defineComponent({
return activeIndex + 1;
});
const childCount = computed(() => state.children.length);
const getContainer = (): HTMLDivElement => self?.proxy?.$el.querySelector('.t-swiper__container');
const getContainer = (): HTMLDivElement => self?.proxy?.$el.querySelector(`.${name}__container`);
// const getContainer = (): HTMLDivElement => swiperContainer.value as any;
const initSwiper = () => {
const _swiperContainer = getContainer();
_swiperContainer.querySelectorAll('.copy-item').forEach((ele) => {
_swiperContainer.removeChild(ele);
});
const items = _swiperContainer.querySelectorAll('.t-swiper-item');
const items = _swiperContainer.querySelectorAll(`.${name}-item`);
state.itemLength = _swiperContainer.children?.length || 0;
const itemWidth = _swiperContainer.querySelector('.t-swiper-item')?.getBoundingClientRect().width || 0;
const itemWidth = _swiperContainer.querySelector(`.${name}-item`)?.getBoundingClientRect().width || 0;
state.itemWidth = itemWidth;
if (items.length <= 0) return false;
if (
Expand Down

0 comments on commit a2d34f7

Please sign in to comment.