Skip to content

Commit

Permalink
test: enhance unit tests for model components coverage to 40%
Browse files Browse the repository at this point in the history
  • Loading branch information
jialudev committed Dec 17, 2024
1 parent 9f0d1be commit 5c2bd0f
Show file tree
Hide file tree
Showing 6 changed files with 290 additions and 92 deletions.
37 changes: 36 additions & 1 deletion frontend/setupTests.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,40 @@
import { config } from '@vue/test-utils';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import SvgIcon from '@/components/shared/SvgIcon.vue';
import * as ElementPlusIconsVue from '@element-plus/icons-vue';
import { createI18n } from 'vue-i18n';
import { createPinia } from 'pinia'
import en from '@/locales/en.js'
import zh from '@/locales/zh.js'

config.global.plugins = [ElementPlus];
const pinia = createPinia();
const i18n = createI18n({
legacy: false,
locale: 'en',
messages: {
en,
zh
}
});

config.global.plugins = [ElementPlus, i18n, pinia];

// register global components
config.global.components = {
SvgIcon,
...ElementPlusIconsVue
};

// gllbal mock

// Mock window.location
const mockLocation = {
href: '',
search: ''
};

Object.defineProperty(window, 'location', {
value: mockLocation,
writable: true
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { describe, it, expect, beforeEach, vi } from "vitest";
import { mount } from "@vue/test-utils";
import ModelRelationsCard from "@/components/models/ModelRelationsCard.vue";
import RepoItem from "@/components/shared/RepoItem.vue";

const createWrapper = (props) => {
return mount(ModelRelationsCard, {
props: {
namespacePath: 'test/namespace',
models: [],
...props
}
});
};

describe("ModelRelationsCard", () => {
it("mounts correctly", () => {
const wrapper = createWrapper();
expect(wrapper.vm).toBeDefined();
});

it("renders correctly with props", () => {
const models = [{
id: 1,
name: 'Model 1',
path: 'user/model-1',
updated_at: '2024-03-20 10:00:00',
downloads: 100
}, {
id: 2,
name: 'Model 2',
path: 'user/model-2',
updated_at: '2024-03-20 10:00:00',
downloads: 200
}];
const wrapper = createWrapper({ models });

expect(wrapper.find('h3').text()).toContain('Model used to traintest/namespace');
expect(wrapper.find('.text-gray-700').text()).toBe('Model used to train');
expect(wrapper.findAllComponents(RepoItem).length).toBe(models.length);
});
});
119 changes: 119 additions & 0 deletions frontend/src/components/__tests__/models/ModelSettings.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { describe, it, expect, beforeEach, vi } from "vitest";
import { mount } from "@vue/test-utils";
import ModelSettings from "@/components/models/ModelSettings.vue";
import { createPinia, setActivePinia } from 'pinia';
import { ElMessage, ElMessageBox } from 'element-plus';

// Mock Element Plus components and functions
vi.mock('element-plus', () => ({
ElMessage: {
install: vi.fn(),
error: vi.fn(),
success: vi.fn(),
warning: vi.fn()
},
ElMessageBox: {
install: vi.fn(),
confirm: vi.fn()
}
}));

// Mock the API response
vi.mock('../../../packs/useFetchApi', () => ({
default: (url) => ({
post: () => ({
json: () => Promise.resolve({
data: { value: { data: { path: 'testuser/testmodel' } } },
error: { value: null }
})
}),
json: () => {
// 根据不同的 URL 返回不同的模拟数据
if (url === '/tags') {
return Promise.resolve({
data: {
value: {
data: [
{ name: 'tag1', category: 'industry', scope: 'model', show_name: 'Tag 1' },
{ name: 'tag2', category: 'industry', scope: 'model', show_name: 'Tag 2' },
{ name: 'tag3', category: 'other', scope: 'model', show_name: 'Tag 3' }
]
}
},
error: { value: null }
})
}
// 默认返回空数据
return Promise.resolve({
data: { value: null },
error: { value: null }
})
}
})
}));

const createWrapper = (props = {}) => {
return mount(ModelSettings, {
props: {
path: "test/model",
modelNickname: "Test Model",
modelDesc: "Test Description",
default_branch: "main",
tagList: [],
tags: {
task_tags: [],
other_tags: [],
industry_tags: []
},
...props
},
});
};

describe("ModelSettings", () => {
beforeEach(() => {
setActivePinia(createPinia());
});

it("mounts correctly", () => {
const wrapper = createWrapper();
expect(wrapper.vm).toBeDefined();
});

it("displays model path correctly", () => {
const wrapper = createWrapper();
expect(wrapper.find('.bg-gray-50').text()).toBe("test/model");
});

it.skip("updates model nickname when button is clicked", async () => {
const wrapper = createWrapper();
await wrapper.setData({ theModelNickname: "New Name" });
await wrapper.find('button').trigger('click');
expect(ElMessage.success).toHaveBeenCalled();
});

it.skip("shows warning when trying to update empty nickname", async () => {
const wrapper = createWrapper();
await wrapper.setData({ theModelNickname: "" });
const updateButton = wrapper.findAll('button').find(btn => btn.text() === 'all.update');
await updateButton.trigger('click');
expect(ElMessage.warning).toHaveBeenCalled();
});

it("handles tag selection correctly", async () => {
const wrapper = createWrapper({
tagList: [{ name: "tag1", show_name: "Tag 1" }]
});
await wrapper.vm.selectTag({ name: "tag1", show_name: "Tag 1" });
expect(wrapper.vm.selectedTags).toHaveLength(1);
});

it("removes tag when close icon is clicked", async () => {
const wrapper = createWrapper();
await wrapper.setData({
selectedTags: [{ name: "tag1", show_name: "Tag 1" }]
});
await wrapper.vm.removeTag("tag1");
expect(wrapper.vm.selectedTags).toHaveLength(0);
});
});
95 changes: 5 additions & 90 deletions frontend/src/components/__tests__/models/NewModel.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,7 @@ const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));

const createWrapper = (props) => {
return mount(NewModel, {
components: {
...ElementPlusIconsVue,
SvgIcon,
},
global: {
// plugins: [ElementPlus],
provide: {
nameRule: /^[a-zA-Z][a-zA-Z0-9-_.]*[a-zA-Z0-9]$/,
}
Expand All @@ -36,56 +31,6 @@ vi.mock('../../../stores/UserStore', () => ({
})
}));

// Mock useFetchApi
const mockPost = vi.fn().mockResolvedValue({
json: async () => ({
data: { value: { data: { path: 'testuser/testmodel' } } },
error: { value: null }
})
});

vi.mock('../../../packs/useFetchApi', () => ({
default: () => {
return {
post: () => ({
json: () => Promise.resolve({
data: { value: { data: { path: 'testuser/testmodel' } } },
error: { value: null }
})
})
};
}
}));

// Mock window.location
const mockLocation = {
href: '',
search: ''
};

Object.defineProperty(window, 'location', {
value: mockLocation,
writable: true
});

// Mock vue-i18n
vi.mock('vue-i18n', () => ({
useI18n: () => ({
t: (key) => key,
})
}));

// testUtils.js
export const createFetchApiMock = (mockResponses = {}) => {
return () => ({
post: () => {
return Promise.resolve({
data: { value: { data: { path: 'testuser/testmodel' } } },
error: { value: null }
});
}
});
};

describe("NewModel", () => {
describe("mount", async () => {
Expand Down Expand Up @@ -133,8 +78,7 @@ describe("NewModel", () => {
describe("form submission", () => {
it("shows success message on successful submission", async () => {
const wrapper = createWrapper();

// 设置表单数据以确保验证通过

wrapper.vm.dataForm = {
owner: 'testuser',
name: 'valid-model',
Expand All @@ -156,42 +100,13 @@ describe("NewModel", () => {
})
}));

await wrapper.vm.handleSubmit(); // 调用提交方法
await delay(2000); // 等待 API 响应
await wrapper.vm.$nextTick(); // 等待 Vue 更新

// 验证成功消息是否被调用
// expect(wrapper.vm.$message).toHaveBeenCalledWith({
// message: '创建成功', // 确保这里的消息与实际消息一致
// type: 'success'
// });
await wrapper.find('button').trigger('click');
await delay(800);
await wrapper.vm.$nextTick();

// 验证 URL 是否正确
// validate href is correct
expect(window.location.href).toBe('/models/testuser/testmodel');
});

it("shows error message on failed submission", async () => {
const wrapper = createWrapper();
// Mock the API response with an error
vi.mock('../../../packs/useFetchApi', () => ({
default: () => ({
post: () => ({
json: () => Promise.resolve({
data: { value: null },
error: { value: { msg: '创建失败' } }
})
})
})
}));

await wrapper.find('button').trigger('click');
await delay(300);
await wrapper.vm.$nextTick()

expect(wrapper.vm.$message).toHaveBeenCalledWith({
message: '创建失败: 创建失败',
type: 'error'
});
});
});
});
Loading

0 comments on commit 5c2bd0f

Please sign in to comment.