feat(blog-review): enhance BlogReviewPage with detailed article retrieval and loading state
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
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();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user