kt-template-admin/test/views/blog/article/preview/preview.spec.tsx

198 lines
5.4 KiB
TypeScript

/* @vitest-environment happy-dom */
/* eslint-disable vue/one-component-per-file */
import { flushPromises, mount } from '@vue/test-utils';
import { defineComponent, h } from 'vue';
import BlogArticlePreview from '@test-source/apps/web-antdv-next/src/views/blog/article/preview/index';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { getArticleDetail } from '#/api/blog';
const testState = vi.hoisted(() => ({
pushSpy: vi.fn(),
route: {
params: {
articleId: '50',
} as Record<string, unknown>,
},
}));
const { pushSpy, route } = testState;
vi.mock('vue-router', () => ({
useRoute: () => route,
useRouter: () => ({
push: pushSpy,
}),
}));
vi.mock('@vben/icons', () => ({
ArrowLeft: defineComponent({
name: 'MockArrowLeft',
setup() {
return () => h('i', { 'data-testid': 'arrow-left' });
},
}),
}));
vi.mock('antdv-next', () => ({
Alert: defineComponent({
name: 'MockAlert',
props: {
message: { default: '', type: String },
type: { default: '', type: String },
},
setup(props) {
return () =>
h('div', { role: 'alert' }, [props.message, props.type as string]);
},
}),
Button: defineComponent({
name: 'MockButton',
props: {
disabled: Boolean,
type: { default: '', type: String },
},
emits: ['click'],
setup(props, { attrs, emit, slots }) {
return () =>
h(
'button',
{
...attrs,
disabled: props.disabled,
type: 'button',
onClick: () => emit('click'),
},
slots.default?.(),
);
},
}),
Space: defineComponent({
name: 'MockSpace',
setup(_, { slots }) {
return () => h('div', slots.default?.());
},
}),
Spin: defineComponent({
name: 'MockSpin',
props: {
spinning: Boolean,
},
setup(props) {
return () => h('div', { 'data-spinning': String(props.spinning) });
},
}),
Tag: defineComponent({
name: 'MockTag',
props: {
color: { default: '', type: String },
},
setup(_, { slots }) {
return () => h('span', slots.default?.());
},
}),
}));
vi.mock('#/api/blog', async () => {
const actual =
await vi.importActual<typeof import('#/api/blog')>('#/api/blog');
return {
...actual,
getArticleDetail: vi.fn(),
};
});
describe('blog article preview page', () => {
beforeEach(() => {
route.params.articleId = '50';
pushSpy.mockReset();
vi.clearAllMocks();
vi.mocked(getArticleDetail).mockResolvedValue({
id: '50',
slug: 'fnos-nas-docker-jenkins-k3d-k8s',
status: 'publish',
title: {
rendered: '飞牛 NAS Docker',
},
});
});
it('loads article detail and renders an iframe-only preview surface', async () => {
const wrapper = mount(BlogArticlePreview);
await flushPromises();
expect(getArticleDetail).toHaveBeenCalledWith('50');
expect(wrapper.find('iframe').attributes('src')).toBe(
'http://127.0.0.1:5173/#/post/fnos-nas-docker-jenkins-k3d-k8s?adminPreview=1&articleId=50',
);
expect(wrapper.find('.blog-article-preview__header').exists()).toBe(false);
expect(wrapper.find('.blog-article-preview__floating-card').exists()).toBe(
true,
);
expect(wrapper.text()).toContain('飞牛 NAS Docker');
expect(wrapper.text()).toContain('文章预览');
expect(wrapper.text()).toContain('已发布');
expect(wrapper.text()).toContain('运行态:已就绪');
expect(wrapper.text()).toContain('127.0.0.1:5173');
});
it('does not call detail API when route article id is missing', async () => {
route.params.articleId = '';
const wrapper = mount(BlogArticlePreview);
await flushPromises();
expect(getArticleDetail).not.toHaveBeenCalled();
expect(wrapper.find('iframe').exists()).toBe(false);
expect(wrapper.text()).toContain('缺少文章 ID');
});
it('routes back to article list from the floating card', async () => {
const wrapper = mount(BlogArticlePreview);
await flushPromises();
await wrapper.find('button').trigger('click');
expect(pushSpy).toHaveBeenCalledWith({ name: 'BlogArticle' });
});
it('reloads article detail from the floating card refresh action', async () => {
const wrapper = mount(BlogArticlePreview);
await flushPromises();
await wrapper.findAll('button')[1]?.trigger('click');
await flushPromises();
expect(getArticleDetail).toHaveBeenCalledTimes(2);
});
it('shows the API error message when article detail loading fails', async () => {
vi.mocked(getArticleDetail).mockRejectedValueOnce(
new Error('detail failed'),
);
const wrapper = mount(BlogArticlePreview);
await flushPromises();
expect(wrapper.find('iframe').exists()).toBe(false);
expect(wrapper.text()).toContain('detail failed');
expect(wrapper.text()).toContain('异常');
});
it('opens the current KT Blog Web preview in a new window', async () => {
const openSpy = vi.spyOn(window, 'open').mockImplementation(() => null);
const wrapper = mount(BlogArticlePreview);
await flushPromises();
await wrapper.find('[data-testid="blog-preview-open"]').trigger('click');
expect(openSpy).toHaveBeenCalledWith(
'http://127.0.0.1:5173/#/post/fnos-nas-docker-jenkins-k3d-k8s?adminPreview=1&articleId=50',
'_blank',
'noopener,noreferrer',
);
});
});