mirror of
https://github.com/vbenjs/vue-vben-admin.git
synced 2025-08-26 16:46:19 +08:00
feat: support vue file unit testing, add some components unit testing (#4119)
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { EllipsisText } from '..';
|
||||
|
||||
describe('ellipsis-text.vue', () => {
|
||||
it('renders the correct content and truncates text', async () => {
|
||||
const wrapper = mount(EllipsisText, {
|
||||
props: {
|
||||
line: 1,
|
||||
title: 'Test Title',
|
||||
},
|
||||
slots: {
|
||||
default: 'This is a very long text that should be truncated.',
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.text()).toContain('This is a very long text');
|
||||
// 检查 ellipsis 是否应用了正确的 class
|
||||
const ellipsis = wrapper.find('.truncate');
|
||||
expect(ellipsis.exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('expands text on click if expand is true', async () => {
|
||||
const wrapper = mount(EllipsisText, {
|
||||
props: {
|
||||
expand: true,
|
||||
line: 1,
|
||||
},
|
||||
slots: {
|
||||
default: 'This is a very long text that should be truncated.',
|
||||
},
|
||||
});
|
||||
|
||||
const ellipsis = wrapper.find('.truncate');
|
||||
await ellipsis.trigger('click');
|
||||
expect(wrapper.emitted('expandChange')).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -51,6 +51,7 @@ interface Props {
|
||||
*/
|
||||
tooltipOverlayStyle?: CSSProperties;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
expand: false,
|
||||
line: 1,
|
||||
|
@@ -0,0 +1,74 @@
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { Page } from '..';
|
||||
|
||||
describe('page.vue', () => {
|
||||
it('renders title when passed', () => {
|
||||
const wrapper = mount(Page, {
|
||||
props: {
|
||||
title: 'Test Title',
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.text()).toContain('Test Title');
|
||||
});
|
||||
|
||||
it('renders description when passed', () => {
|
||||
const wrapper = mount(Page, {
|
||||
props: {
|
||||
description: 'Test Description',
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.text()).toContain('Test Description');
|
||||
});
|
||||
|
||||
it('renders default slot content', () => {
|
||||
const wrapper = mount(Page, {
|
||||
slots: {
|
||||
default: '<p>Default Slot Content</p>',
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.html()).toContain('<p>Default Slot Content</p>');
|
||||
});
|
||||
|
||||
it('renders footer slot when showFooter is true', () => {
|
||||
const wrapper = mount(Page, {
|
||||
props: {
|
||||
showFooter: true,
|
||||
},
|
||||
slots: {
|
||||
footer: '<p>Footer Slot Content</p>',
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.html()).toContain('<p>Footer Slot Content</p>');
|
||||
});
|
||||
|
||||
it('applies the custom contentClass', () => {
|
||||
const wrapper = mount(Page, {
|
||||
props: {
|
||||
contentClass: 'custom-class',
|
||||
},
|
||||
});
|
||||
|
||||
const contentDiv = wrapper.find('.m-4');
|
||||
expect(contentDiv.classes()).toContain('custom-class');
|
||||
});
|
||||
|
||||
it('does not render description slot if description prop is provided', () => {
|
||||
const wrapper = mount(Page, {
|
||||
props: {
|
||||
description: 'Test Description',
|
||||
},
|
||||
slots: {
|
||||
description: '<p>Description Slot Content</p>',
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.text()).toContain('Test Description');
|
||||
expect(wrapper.html()).not.toContain('Description Slot Content');
|
||||
});
|
||||
});
|
@@ -1,13 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
defineOptions({
|
||||
name: 'PageFooter',
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="bg-card align-center absolute bottom-0 left-0 right-0 flex px-6 py-4"
|
||||
>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
@@ -1,18 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { PageHeaderProps } from './page.ts';
|
||||
|
||||
defineOptions({
|
||||
name: 'PageHeader',
|
||||
});
|
||||
|
||||
const props = defineProps<PageHeaderProps>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-card px-6 py-4">
|
||||
<div class="mb-2 flex justify-between text-xl font-bold leading-10">
|
||||
{{ props.title }}
|
||||
</div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
@@ -1,11 +0,0 @@
|
||||
interface PageHeaderProps {
|
||||
title?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
interface Props extends PageHeaderProps {
|
||||
contentClass?: string;
|
||||
showFooter?: boolean;
|
||||
}
|
||||
|
||||
export type { PageHeaderProps, Props };
|
@@ -1,14 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import type { Props } from './page';
|
||||
|
||||
import PageFooter from './page-footer.vue';
|
||||
import PageHeader from './page-header.vue';
|
||||
interface Props {
|
||||
title?: string;
|
||||
description?: string;
|
||||
contentClass?: string;
|
||||
showFooter?: boolean;
|
||||
}
|
||||
|
||||
defineOptions({
|
||||
name: 'Page',
|
||||
});
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
contentClass: '',
|
||||
description: '',
|
||||
showFooter: false,
|
||||
title: '',
|
||||
@@ -17,22 +20,26 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
|
||||
<template>
|
||||
<div class="relative h-full">
|
||||
<PageHeader
|
||||
<div
|
||||
v-if="description || $slots.description || title"
|
||||
:title="props.title"
|
||||
class="bg-card px-6 py-4"
|
||||
>
|
||||
<template #default>
|
||||
<template v-if="description">{{ description }}</template>
|
||||
<slot v-else name="description"></slot>
|
||||
</template>
|
||||
</PageHeader>
|
||||
<div class="mb-2 flex justify-between text-xl font-bold leading-10">
|
||||
{{ title }}
|
||||
</div>
|
||||
<template v-if="description">{{ description }}</template>
|
||||
<slot v-else name="description"></slot>
|
||||
</div>
|
||||
|
||||
<div :class="contentClass" class="m-4">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<PageFooter v-if="props.showFooter">
|
||||
<template #default>
|
||||
<slot name="footer"></slot>
|
||||
</template>
|
||||
</PageFooter>
|
||||
|
||||
<div
|
||||
v-if="props.showFooter"
|
||||
class="bg-card align-center absolute bottom-0 left-0 right-0 flex px-6 py-4"
|
||||
>
|
||||
<slot name="footer"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import type { LoginCodeEmits } from './typings';
|
||||
import type { LoginCodeEmits } from './types';
|
||||
|
||||
import { computed, onBeforeUnmount, reactive, ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
@@ -8,4 +8,4 @@ export type {
|
||||
AuthenticationProps,
|
||||
LoginAndRegisterParams,
|
||||
LoginCodeParams,
|
||||
} from './typings';
|
||||
} from './types';
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import type { AuthenticationProps, LoginAndRegisterParams } from './typings';
|
||||
import type { AuthenticationProps, LoginAndRegisterParams } from './types';
|
||||
|
||||
import { useForwardPropsEmits } from '@vben/hooks';
|
||||
import {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import type { AuthenticationProps, LoginEmits } from './typings';
|
||||
import type { AuthenticationProps, LoginEmits } from './types';
|
||||
|
||||
import { computed, reactive } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import type { RegisterEmits } from './typings';
|
||||
import type { RegisterEmits } from './types';
|
||||
|
||||
import { computed, reactive } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
@@ -1,6 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
import type { RegisterEmits } from './typings';
|
||||
|
||||
import { computed, reactive } from 'vue';
|
||||
|
||||
import {
|
||||
@@ -19,6 +17,14 @@ interface Props {
|
||||
text?: string;
|
||||
}
|
||||
|
||||
interface LockAndRegisterParams {
|
||||
lockScreenPassword: string;
|
||||
}
|
||||
|
||||
interface RegisterEmits {
|
||||
submit: [LockAndRegisterParams];
|
||||
}
|
||||
|
||||
defineOptions({
|
||||
name: 'LockScreenModal',
|
||||
});
|
||||
|
@@ -1,9 +0,0 @@
|
||||
interface LockAndRegisterParams {
|
||||
lockScreenPassword: string;
|
||||
}
|
||||
|
||||
interface RegisterEmits {
|
||||
submit: [LockAndRegisterParams];
|
||||
}
|
||||
|
||||
export type { LockAndRegisterParams, RegisterEmits };
|
Reference in New Issue
Block a user