116 lines
5.0 KiB
TypeScript
116 lines
5.0 KiB
TypeScript
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
import { screen, fireEvent, waitFor } from '@testing-library/react';
|
|
import { renderWithProviders } from '@/test/utils';
|
|
|
|
vi.mock('sonner', () => ({ toast: { success: vi.fn(), error: vi.fn() } }));
|
|
vi.mock('@/lib/api', () => ({
|
|
api: { get: vi.fn(), post: vi.fn(), patch: vi.fn(), put: vi.fn(), delete: vi.fn() },
|
|
ApiError: class extends Error {},
|
|
}));
|
|
|
|
import { api } from '@/lib/api';
|
|
import BlogReviewPage from '@/pages/BlogReviewPage';
|
|
|
|
const get = api.get as ReturnType<typeof vi.fn>;
|
|
|
|
/** ردیف صف — همان شکل `toListArray()`: بدون body و faq و sources. */
|
|
const ROW = {
|
|
uuid: 'b-1',
|
|
title: 'لیزر موهای زائد چطور کار میکند؟',
|
|
slug: 'laser',
|
|
summary: 'خلاصهٔ کوتاه',
|
|
image_url: null,
|
|
tags: ['پوست'],
|
|
status: 'draft',
|
|
review_status: 'pending_review',
|
|
topic_slug: 'skin/laser',
|
|
created_at: 1_700_000_000,
|
|
};
|
|
|
|
/** پاسخ اندپوینت جزئیات — double-nested، همان قرارداد فرم ویرایش مقاله. */
|
|
const DETAIL = {
|
|
success: true,
|
|
data: {
|
|
data: {
|
|
...ROW,
|
|
body: '<p>متن کامل مقاله اینجاست.</p>',
|
|
reading_time: 7,
|
|
primary_keyword: 'لیزر موهای زائد',
|
|
faq: [{ q: 'چند جلسه لازم است؟', a: 'معمولاً شش تا هشت جلسه.' }],
|
|
sources: [{ url: 'https://example.org/a', title: 'راهنمای پوست' }],
|
|
},
|
|
},
|
|
};
|
|
|
|
function mockApi(detail: unknown = DETAIL) {
|
|
get.mockImplementation((url: string) => {
|
|
if (url.includes('/review-queue')) {
|
|
return Promise.resolve({
|
|
success: true,
|
|
data: [ROW],
|
|
meta: { totalRecords: 1, totalPages: 1, currentPage: 1 },
|
|
errors: [],
|
|
});
|
|
}
|
|
return Promise.resolve(detail);
|
|
});
|
|
}
|
|
|
|
describe('BlogReviewPage — مودال بازبینی', () => {
|
|
beforeEach(() => { get.mockReset(); mockApi(); });
|
|
|
|
/**
|
|
* صف با `toListArray()` ساخته میشود و `body` ندارد؛ مودال باید متن کامل را از
|
|
* اندپوینت جزئیات بگیرد، وگرنه بازبین یک قاب خالی میبیند.
|
|
*/
|
|
it('با کلیک روی بازبینی، متن کامل مقاله را میگیرد و نشان میدهد', async () => {
|
|
renderWithProviders(<BlogReviewPage />, { route: '/admin/blog-review' });
|
|
|
|
fireEvent.click(await screen.findByRole('button', { name: 'بازبینی' }));
|
|
|
|
await waitFor(() => expect(screen.getByText('متن کامل مقاله اینجاست.')).toBeInTheDocument());
|
|
expect(get).toHaveBeenCalledWith('/api/v1/admin/blog/b-1');
|
|
});
|
|
|
|
it('پرسشهای متداول و منابع و برچسبها را هم نشان میدهد', async () => {
|
|
renderWithProviders(<BlogReviewPage />, { route: '/admin/blog-review' });
|
|
|
|
fireEvent.click(await screen.findByRole('button', { name: 'بازبینی' }));
|
|
|
|
await waitFor(() => expect(screen.getByText('پرسشهای متداول')).toBeInTheDocument());
|
|
expect(screen.getByText('چند جلسه لازم است؟')).toBeInTheDocument();
|
|
expect(screen.getByRole('link', { name: 'راهنمای پوست' })).toHaveAttribute('href', 'https://example.org/a');
|
|
expect(screen.getByText('پوست')).toBeInTheDocument();
|
|
});
|
|
|
|
/** بازبین باید بداند چه چیزی را تأیید میکند: زمان مطالعه و کلیدواژه و موضوع. */
|
|
it('شناسنامهٔ مقاله را بالای متن میآورد', async () => {
|
|
renderWithProviders(<BlogReviewPage />, { route: '/admin/blog-review' });
|
|
|
|
fireEvent.click(await screen.findByRole('button', { name: 'بازبینی' }));
|
|
|
|
await waitFor(() => expect(screen.getByText('۷ دقیقه مطالعه')).toBeInTheDocument());
|
|
expect(screen.getByText('کلیدواژه: لیزر موهای زائد')).toBeInTheDocument();
|
|
});
|
|
|
|
/** تا نیامدنِ متن، دکمههای تصمیم نباید روی یک قاب خالی بنشینند. */
|
|
it('پیش از رسیدن متن، حالت بارگذاری نشان میدهد', async () => {
|
|
get.mockImplementation((url: string) => {
|
|
if (url.includes('/review-queue')) {
|
|
return Promise.resolve({
|
|
success: true,
|
|
data: [ROW],
|
|
meta: { totalRecords: 1, totalPages: 1, currentPage: 1 },
|
|
errors: [],
|
|
});
|
|
}
|
|
return new Promise(() => {}); // هرگز resolve نمیشود
|
|
});
|
|
|
|
renderWithProviders(<BlogReviewPage />, { route: '/admin/blog-review' });
|
|
fireEvent.click(await screen.findByRole('button', { name: 'بازبینی' }));
|
|
|
|
expect(await screen.findByText('در حال بارگذاری مقاله...')).toBeInTheDocument();
|
|
});
|
|
});
|