test(e2e): expand Playwright test coverage with shop filters, tracking, cart operations and admin modules
This commit is contained in:
parent
270e491d08
commit
3c325c412c
72
test-coverage-map.md
Normal file
72
test-coverage-map.md
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
# نقشه جامع پوشش تستهای سرتاسری (E2E Test Coverage Map) — پروژه Canina
|
||||||
|
|
||||||
|
این سند شامل استخراج و فهرست کامل تمامی قابلیتهای کاربرمحور، فرمها، جریانهای خرید و احراز هویت، عملیات CRUD پنل مدیریت و حالتهای مرزی (Edge Cases) در پلتفرم Canina است.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ۱. معماری و نقشهای کاربری (User Roles)
|
||||||
|
1. **مهمان / کاربر عمومی (Guest / Public User)**:
|
||||||
|
- مشاهده لندینگ اصلی و بنرها
|
||||||
|
- جستجو و فیلتر محصولات در آرشیو فروشگاه (`/shop`)
|
||||||
|
- مشاهده صفحه جزئیات محصول (PDP)، دوزبندی و مشخصات مکمل
|
||||||
|
- افزودن به سبد خرید و مدیریت مقادیر در Drawer سبد خرید
|
||||||
|
- جریان تسویهحساب (Checkout Flow) با ثبت آدرس و اطلاعات تماس
|
||||||
|
- ثبت درخواست همکاری تجاری / داروخانهای (`/b2b`)
|
||||||
|
- ثبت فرم تماس و استعلام مشاوره دارویی (`/contact`)
|
||||||
|
- پیگیری وضعیت سفارش با کد پیگیری (`/track`)
|
||||||
|
- مطالعه وبلاگ (`/blog`) و مقالات دانشنامه دارویی (`/wiki/[slug]`)
|
||||||
|
- مشاهده صفحه ویدیوهای آموزشی (`/videos`)
|
||||||
|
- جستجوی سراسری محصولات و مقالات (`/search`)
|
||||||
|
|
||||||
|
2. **کاربر ثبتنامشده / صاحب پت (Authenticated User / Pet Owner)**:
|
||||||
|
- ورود/ثبتنام با شماره موبایل و کد تایید OTP (`/profile` یا مودال ورود)
|
||||||
|
- مدیریت پروفایل کاربری و آدرسها
|
||||||
|
- مدیریت پروفایل پتها (سگ/گربه، نژاد، وزن، سن، بیماریهای زمینهای)
|
||||||
|
- داشبورد سوابق سفارشات و نسخهها (`/dashboard`)
|
||||||
|
- ثبت تیکت پشتیبانی و استعلام دارویی
|
||||||
|
|
||||||
|
3. **مدیر ارشد و کادر مدیریت (Admin / Store Manager)**:
|
||||||
|
- احراز هویت با ایمیل/رمز عبور یا OTP در پنل مدیریت (`/login`)
|
||||||
|
- مدیریت محصولات (ایجاد، ویرایش، حذف، قیمتگذاری، کنترل موجودی، گالری، فیلدهای پیشرفته سئو SERP)
|
||||||
|
- مدیریت دستهبندیها و ویژگیهای درمانی
|
||||||
|
- مدیریت مقالات وبلاگ و دانشنامه تخصصی (Wiki)
|
||||||
|
- مدیریت و تغییر وضعیت سفارشات (Orders)
|
||||||
|
- بررسی و تایید درخواستهای همکاری تجاری B2B و فرمهای تماس
|
||||||
|
- تنظیمات عمومی سایت، درگاههای پرداخت، پیامک و سئو
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ۲. ماتریس جریانها و قابلیتهای کاربرمحور (Feature & Flow Matrix)
|
||||||
|
|
||||||
|
| کد | بخش / صفحه | قابلیت / سناریو | Happy Path | ورودی نامعتبر / منفی | حالت مرزی / Edge Case | وضعیت فعلی تست |
|
||||||
|
|---|---|---|---|---|---|---|
|
||||||
|
| **ST-01** | صفحه اصلی (`/`) | بارگذاری هدر، منوها، بنر اسلایدر، محصولات برگزیده | ✅ | ❌ | لود در شبکه کند | ✅ تست ضمنی |
|
||||||
|
| **ST-02** | فروشگاه (`/shop`) | فیلتر دستهبندی، مرتبسازی، جستجو و صفحهبندی | ✅ | جستجوی بدون نتیجه | فیلتر همزمان چندگانه | ⚠️ ناقص |
|
||||||
|
| **ST-03** | جزئیات محصول (`/shop/[slug]`) | تغییر وزن/دوز، ماشینحساب مصرف پت، افزودن به سبد | ✅ | محصول ناموجود | گالری تصاویر چندگانه | ⚠️ ناقص |
|
||||||
|
| **ST-04** | سبد خرید (Cart Drawer) | افزایش/کاهش تعداد، حذف آیتم، محاسبه جمع کل | ✅ | سبد خرید خالی | کوپن تخفیف نامعتبر | ⚠️ ناقص |
|
||||||
|
| **ST-05** | تسویه حساب (`/checkout`) | ثبت اطلاعات گیرنده، انتخاب روش ارسال و پرداخت | ✅ | اعتبارسنجی فیلدهای ضروری | لغو پرداخت و بازگشت | ✅ پوشش داده شد |
|
||||||
|
| **ST-06** | همکاری تجاری (`/b2b`) | ارسال فرم همکاری کلینیک/پتشاپ با مدارک | ✅ | فیلدهای ناقص/موبایل اشتباه | ارسال فرم تکراری | ✅ پوشش داده شد |
|
||||||
|
| **ST-07** | تماس با ما (`/contact`) | ارسال پیام مشاوره و استعلام دارویی | ✅ | ایمیل/موبایل نامعتبر | فیلد پیام خالی | ✅ پوشش داده شد |
|
||||||
|
| **ST-08** | وبلاگ و دانشنامه (`/blog`, `/wiki`) | مشاهده مقالات، جستجو، جدول ترکیبات دارویی | ✅ | اسلاگ ناموجود (404) | لینکهای بینمقالات | ✅ پوشش داده شد |
|
||||||
|
| **ST-09** | پیگیری سفارش (`/track`) | استعلام وضعیت سفارش با شماره موبایل و کد سفارش | ✅ | کد پیگیری نامعتبر | سفارش یافت نشد | ❌ بدون تست |
|
||||||
|
| **ST-10** | جستجوی سراسری (`/search`) | سرچ زنده محصولات، پیشنهادات هوشمند | ✅ | کلمه کلیدی بدون نتیجه | عبارات خاص فارسی/انگلیسی | ❌ بدون تست |
|
||||||
|
| **ST-11** | احراز هویت کاربر (`/profile`) | لاگین OTP، دریافت پیامک، ورود و مدیریت پتها | ✅ | کد OTP اشتباه یا منقضی | خروج از حساب (Logout) | ❌ بدون تست |
|
||||||
|
| **AD-01** | لاگین ادمین (`/login`) | ورود با رمز عبور و سشن توکن JWT | ✅ | رمز اشتباه / دسترسی غیرمجاز | سشن منقضیشده | ✅ پوشش داده شد |
|
||||||
|
| **AD-02** | مدیریت محصولات (`/products`) | ساخت، ویرایش، تب سئو، آپلود تصویر و حذف | ✅ | نام خالی / فرمت اشتباه | مقادیر طولانی سئو | ✅ پوشش داده شد |
|
||||||
|
| **AD-03** | مدیریت سفارشات (`/orders`) | فیلتر وضعیت، مشاهده جزئیات، تغییر وضعیت به ارسالشده | ✅ | تغییر غیرمجاز وضعیت | لیست سفارش خالی | ❌ بدون تست |
|
||||||
|
| **AD-04** | مدیریت B2B (`/b2b-requests`) | مشاهده درخواستهای دریافتی، تغییر وضعیت و یادداشت | ✅ | بدون داده | تایید نهایی همکاری | ❌ بدون تست |
|
||||||
|
| **AD-05** | مدیریت مقالات (`/blogs`) | ایجاد مقاله، ویرایش تگها، وضعیت انتشار | ✅ | عنوان خالی | ویرایش محتوای حجیم | ❌ بدون تست |
|
||||||
|
| **AD-06** | تنظیمات سئو و عمومی (`/settings`) | تغییر متادیتا، فعال/غیرفعال کردن حالت کاتالوگ | ✅ | ساختار نامعتبر JSON | ذخیره فوری تنظیمات | ❌ بدون تست |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ۳. برنامه تکمیل و توسعه تستهای مفقود (Action Plan)
|
||||||
|
برای رسیدن به پوشش ۱۰۰٪ سیستم، فایلهای تست جدید زیر باید ایجاد و تستهای موجود با رویکرد پایداری انتخابگرها و پوشش سناریوهای منفی بازنویسی شوند:
|
||||||
|
|
||||||
|
1. `tests/e2e/storefront/shop-filter-search.spec.ts`: تست کامل جستجو، فیلتر دستهها، ماشینحساب دوز و حالتهای بدون نتیجه.
|
||||||
|
2. `tests/e2e/storefront/cart-operations.spec.ts`: تست تعاملی سبد خرید، تغییر تعداد، حذف، کوپن تخفیف و سقف خرید.
|
||||||
|
3. `tests/e2e/storefront/order-tracking.spec.ts`: تست پیگیری سفارشات با سناریوهای موفق و ناموفق.
|
||||||
|
4. `tests/e2e/storefront/user-auth-pets.spec.ts`: تست ورود کاربر، OTP، اضافه کردن پت جدید و پروفایل.
|
||||||
|
5. `tests/e2e/admin/admin-orders-flow.spec.ts`: تست مدیریت سفارشات، تغییر وضعیت فاکتور و فیلترها در پنل مدیریت.
|
||||||
|
6. `tests/e2e/admin/admin-blogs-crud.spec.ts`: تست ایجاد و ویرایش مقالات وبلاگ و دانشنامه در پنل ادمین.
|
||||||
|
7. `tests/e2e/admin/admin-b2b-submissions.spec.ts`: تست مشاهده و مدیریت فرمهای ثبت شده B2B و تماس.
|
||||||
86
tests/e2e/admin/admin-b2b-submissions.spec.ts
Normal file
86
tests/e2e/admin/admin-b2b-submissions.spec.ts
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import { test, expect } from '../../fixtures';
|
||||||
|
|
||||||
|
test.describe('سناریوی مدیریت درخواستهای B2B و فرمهای تماس (Admin B2B Submissions Flow)', () => {
|
||||||
|
test('ورود به بخش B2B -> مشاهده درخواستها -> فیلتر و بررسی جزئیات', async ({ page }) => {
|
||||||
|
// Intercept and mock Admin Auth & B2B API calls
|
||||||
|
await page.route('**/api/auth/admin-login*', async (route) => {
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: 'application/json',
|
||||||
|
body: JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
accessToken: 'mock_e2e_admin_jwt_token',
|
||||||
|
user: { id: 'admin-1', email: 'admin@canina.ir', role: 'SUPER_ADMIN' },
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.route('**/api/auth/refresh*', async (route) => {
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: 'application/json',
|
||||||
|
body: JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
accessToken: 'mock_e2e_admin_jwt_token',
|
||||||
|
user: { id: 'admin-1', email: 'admin@canina.ir', role: 'SUPER_ADMIN' },
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.route('**/api/users/profile*', async (route) => {
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: 'application/json',
|
||||||
|
body: JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
data: { id: 'admin-1', email: 'admin@canina.ir', role: 'SUPER_ADMIN' },
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.route('**/api/admin/b2b-applications*', async (route) => {
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: 'application/json',
|
||||||
|
body: JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
id: 'b2b-1',
|
||||||
|
clinicName: 'کلینیک تخصصی دامپزشکی پایتخت',
|
||||||
|
managerName: 'دکتر محمدی',
|
||||||
|
phone: '09123334455',
|
||||||
|
status: 'PENDING',
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
meta: { total: 1, lastPage: 1 },
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 1. لاگین ادمین
|
||||||
|
await page.goto('/login', { waitUntil: 'domcontentloaded' });
|
||||||
|
const emailInput = page.locator('input[type="email"], input[placeholder*="ایمیل"]').first();
|
||||||
|
const passwordInput = page.locator('input[type="password"], input[placeholder*="رمز"]').first();
|
||||||
|
if (await emailInput.isVisible()) {
|
||||||
|
await emailInput.fill('admin@canina.ir');
|
||||||
|
await passwordInput.fill('Admin@123456');
|
||||||
|
const submitBtn = page.getByRole('button', { name: /ورود به پنل|ورود/i }).first();
|
||||||
|
await submitBtn.click();
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. ورود به صفحه مدیریت درخواستهای B2B
|
||||||
|
await page.goto('/b2b', { waitUntil: 'domcontentloaded' });
|
||||||
|
await expect(page).toHaveURL(/\/b2b/);
|
||||||
|
|
||||||
|
// 3. بررسی ساختار لودینگ صفحه یا جدول اطلاعات
|
||||||
|
const b2bHeader = page.locator('h1, h2, h3, div').filter({ hasText: /درخواست|B2B|همکاری|کلینیک|پتشاپ/i }).first();
|
||||||
|
await expect(b2bHeader).toBeVisible({ timeout: 15000 });
|
||||||
|
});
|
||||||
|
});
|
||||||
102
tests/e2e/admin/admin-blogs-crud.spec.ts
Normal file
102
tests/e2e/admin/admin-blogs-crud.spec.ts
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import { test, expect } from '../../fixtures';
|
||||||
|
|
||||||
|
test.describe('سناریوی مدیریت مقالات و وبلاگ در پنل ادمین (Admin Blogs & CMS Flow)', () => {
|
||||||
|
test('ورود به بخش مقالات -> باز کردن فرم ایجاد -> پر کردن عنوان و محتوا -> ذخیره', async ({ page }) => {
|
||||||
|
// Intercept and mock Admin Auth & Blogs API calls
|
||||||
|
await page.route('**/api/auth/admin-login*', async (route) => {
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: 'application/json',
|
||||||
|
body: JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
accessToken: 'mock_e2e_admin_jwt_token',
|
||||||
|
user: { id: 'admin-1', email: 'admin@canina.ir', role: 'SUPER_ADMIN' },
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.route('**/api/auth/refresh*', async (route) => {
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: 'application/json',
|
||||||
|
body: JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
accessToken: 'mock_e2e_admin_jwt_token',
|
||||||
|
user: { id: 'admin-1', email: 'admin@canina.ir', role: 'SUPER_ADMIN' },
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.route('**/api/users/profile*', async (route) => {
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: 'application/json',
|
||||||
|
body: JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
data: { id: 'admin-1', email: 'admin@canina.ir', role: 'SUPER_ADMIN' },
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.route('**/api/admin/blogs*', async (route) => {
|
||||||
|
if (route.request().method() === 'GET') {
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: 'application/json',
|
||||||
|
body: JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
id: 'blog-1',
|
||||||
|
titleFa: 'راهنمای جامع سلامت مفاصل سگ و گربه',
|
||||||
|
slug: 'dog-joint-health-guide',
|
||||||
|
status: 'PUBLISHED',
|
||||||
|
views: 1250,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
meta: { total: 1, lastPage: 1 },
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await route.fulfill({
|
||||||
|
status: 201,
|
||||||
|
contentType: 'application/json',
|
||||||
|
body: JSON.stringify({ success: true, message: 'مقاله با موفقیت ایجاد شد' }),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 1. لاگین ادمین
|
||||||
|
await page.goto('/login', { waitUntil: 'domcontentloaded' });
|
||||||
|
const emailInput = page.locator('input[type="email"], input[placeholder*="ایمیل"]').first();
|
||||||
|
const passwordInput = page.locator('input[type="password"], input[placeholder*="رمز"]').first();
|
||||||
|
if (await emailInput.isVisible()) {
|
||||||
|
await emailInput.fill('admin@canina.ir');
|
||||||
|
await passwordInput.fill('Admin@123456');
|
||||||
|
const submitBtn = page.getByRole('button', { name: /ورود به پنل|ورود/i }).first();
|
||||||
|
await submitBtn.click();
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. ورود به صفحه مدیریت مقالات وبلاگ
|
||||||
|
await page.goto('/blogs', { waitUntil: 'domcontentloaded' });
|
||||||
|
await expect(page).toHaveURL(/\/blogs/);
|
||||||
|
|
||||||
|
// 3. بررسی دکمه ایجاد مقاله جدید
|
||||||
|
const newBlogBtn = page.getByRole('button', { name: /مقاله جدید|افزودن مقاله|پست جدید/i }).or(
|
||||||
|
page.locator('button').filter({ hasText: /جدید|افزودن/i })
|
||||||
|
).first();
|
||||||
|
await expect(newBlogBtn).toBeVisible({ timeout: 15000 });
|
||||||
|
await newBlogBtn.click();
|
||||||
|
|
||||||
|
// 4. بررسی فرم ایجاد مقاله
|
||||||
|
const blogTitleInput = page.locator('input[placeholder*="عنوان"], input[name*="title"]').first();
|
||||||
|
if (await blogTitleInput.isVisible()) {
|
||||||
|
await blogTitleInput.fill('مقاله آموزشی تستی تستهای سرتاسری Playwright');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
94
tests/e2e/admin/admin-orders-flow.spec.ts
Normal file
94
tests/e2e/admin/admin-orders-flow.spec.ts
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
import { test, expect } from '../../fixtures';
|
||||||
|
|
||||||
|
test.describe('سناریوی مدیریت سفارشات و تغییر وضعیت در پنل ادمین (Admin Orders Management)', () => {
|
||||||
|
test('ورود به بخش سفارشات -> فیلتر وضعیت -> مشاهده جزئیات فاکتور', async ({ page }) => {
|
||||||
|
// Intercept and mock Admin Auth & Orders API calls
|
||||||
|
await page.route('**/api/auth/admin-login*', async (route) => {
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: 'application/json',
|
||||||
|
body: JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
accessToken: 'mock_e2e_admin_jwt_token',
|
||||||
|
user: { id: 'admin-1', email: 'admin@canina.ir', role: 'SUPER_ADMIN' },
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.route('**/api/auth/refresh*', async (route) => {
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: 'application/json',
|
||||||
|
body: JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
accessToken: 'mock_e2e_admin_jwt_token',
|
||||||
|
user: { id: 'admin-1', email: 'admin@canina.ir', role: 'SUPER_ADMIN' },
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.route('**/api/users/profile*', async (route) => {
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: 'application/json',
|
||||||
|
body: JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
data: { id: 'admin-1', email: 'admin@canina.ir', role: 'SUPER_ADMIN' },
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.route('**/api/admin/orders*', async (route) => {
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: 'application/json',
|
||||||
|
body: JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
id: 'ord-101',
|
||||||
|
orderNumber: 'CN-10001',
|
||||||
|
customerName: 'علی رضایی',
|
||||||
|
customerPhone: '09121112233',
|
||||||
|
totalAmount: 1850000,
|
||||||
|
status: 'PROCESSING',
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
items: [{ id: 'item-1', productName: 'مکمل مفاصل سگ', quantity: 2, price: 925000 }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
meta: { total: 1, lastPage: 1 },
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 1. لاگین ادمین
|
||||||
|
await page.goto('/login', { waitUntil: 'domcontentloaded' });
|
||||||
|
const emailInput = page.locator('input[type="email"], input[placeholder*="ایمیل"]').first();
|
||||||
|
const passwordInput = page.locator('input[type="password"], input[placeholder*="رمز"]').first();
|
||||||
|
if (await emailInput.isVisible()) {
|
||||||
|
await emailInput.fill('admin@canina.ir');
|
||||||
|
await passwordInput.fill('Admin@123456');
|
||||||
|
const submitBtn = page.getByRole('button', { name: /ورود به پنل|ورود/i }).first();
|
||||||
|
await submitBtn.click();
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. ورود به صفحه مدیریت سفارشات
|
||||||
|
await page.goto('/orders', { waitUntil: 'domcontentloaded' });
|
||||||
|
await expect(page).toHaveURL(/\/orders/);
|
||||||
|
|
||||||
|
// 3. بررسی تیتر صفحه و لیست سفارشات
|
||||||
|
const ordersHeader = page.locator('h1, h2, div').filter({ hasText: /سفارشات|مدیریت سفارش/i }).first();
|
||||||
|
await expect(ordersHeader).toBeVisible({ timeout: 15000 });
|
||||||
|
|
||||||
|
// 4. تست فیلترهای وضعیت سفارش (در حال پردازش، ارسال شده، تکمیل شده)
|
||||||
|
const statusFilter = page.locator('select, button').filter({ hasText: /وضعیت|همه|پردازش/i }).first();
|
||||||
|
if (await statusFilter.isVisible()) {
|
||||||
|
await statusFilter.click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
36
tests/e2e/storefront/cart-operations.spec.ts
Normal file
36
tests/e2e/storefront/cart-operations.spec.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { test, expect } from '../../fixtures';
|
||||||
|
|
||||||
|
test.describe('سناریوی عملیات پیشرفته سبد خرید (Cart Drawer Interactive Operations)', () => {
|
||||||
|
test('افزودن کالا به سبد -> افزایش و کاهش تعداد -> حذف کالا -> بررسی حالت خالی', async ({ page }) => {
|
||||||
|
// 1. ورود به فروشگاه
|
||||||
|
await page.goto('/shop', { waitUntil: 'domcontentloaded' });
|
||||||
|
await expect(page).toHaveURL(/\/shop/);
|
||||||
|
|
||||||
|
// 2. افزودن محصول به سبد خرید
|
||||||
|
const buyButton = page.locator('button').filter({ hasText: /خرید|افزودن به سبد/i }).first();
|
||||||
|
await expect(buyButton).toBeVisible({ timeout: 15000 });
|
||||||
|
await buyButton.click();
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
|
||||||
|
// 3. تست افزایش تعداد با دکمه +
|
||||||
|
const plusButton = page.locator('button').filter({ hasText: '+' }).first();
|
||||||
|
if (await plusButton.isVisible()) {
|
||||||
|
await plusButton.click();
|
||||||
|
await page.waitForTimeout(300);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. تست کاهش تعداد با دکمه -
|
||||||
|
const minusButton = page.locator('button').filter({ hasText: '-' }).first();
|
||||||
|
if (await minusButton.isVisible()) {
|
||||||
|
await minusButton.click();
|
||||||
|
await page.waitForTimeout(300);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. باز کردن دراور یا صفحه تسویهحساب
|
||||||
|
await page.goto('/checkout', { waitUntil: 'domcontentloaded' });
|
||||||
|
await expect(page).toHaveURL(/\/checkout/);
|
||||||
|
|
||||||
|
const checkoutSummary = page.locator('button, div, h2, span').filter({ hasText: /پرداخت|فاکتور|سبد خرید|مجموع/i }).first();
|
||||||
|
await expect(checkoutSummary).toBeVisible({ timeout: 10000 });
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -24,6 +24,7 @@ test.describe('سناریوی خرید و سبد خرید (Storefront Checkout F
|
|||||||
const buyButtonOnCard = page.locator('button').filter({ hasText: /خرید/i }).first();
|
const buyButtonOnCard = page.locator('button').filter({ hasText: /خرید/i }).first();
|
||||||
if (await buyButtonOnCard.isVisible()) {
|
if (await buyButtonOnCard.isVisible()) {
|
||||||
await buyButtonOnCard.click();
|
await buyButtonOnCard.click();
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
} else {
|
} else {
|
||||||
const href = await productCard.getAttribute('href');
|
const href = await productCard.getAttribute('href');
|
||||||
if (href) {
|
if (href) {
|
||||||
@ -38,6 +39,7 @@ test.describe('سناریوی خرید و سبد خرید (Storefront Checkout F
|
|||||||
).first();
|
).first();
|
||||||
await expect(addToCartBtn).toBeVisible({ timeout: 15000 });
|
await expect(addToCartBtn).toBeVisible({ timeout: 15000 });
|
||||||
await addToCartBtn.click();
|
await addToCartBtn.click();
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. رفتن به صفحه تسویه حساب (Checkout)
|
// 5. رفتن به صفحه تسویه حساب (Checkout)
|
||||||
|
|||||||
31
tests/e2e/storefront/order-tracking.spec.ts
Normal file
31
tests/e2e/storefront/order-tracking.spec.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { test, expect } from '../../fixtures';
|
||||||
|
|
||||||
|
test.describe('سناریوی پیگیری سفارشات (Order Tracking Flow & Edge Cases)', () => {
|
||||||
|
test('ورود به صفحه پیگیری -> استعلام کد نامعتبر (Edge Case) -> استعلام سفارش معتبر', async ({ page }) => {
|
||||||
|
// 1. ورود به صفحه /track
|
||||||
|
await page.goto('/track', { waitUntil: 'domcontentloaded' });
|
||||||
|
await expect(page).toHaveURL(/\/track/);
|
||||||
|
|
||||||
|
// 2. بررسی وجود المانهای پیگیری و فیلد ورودی
|
||||||
|
const trackingInput = page.locator('input[placeholder*="شماره سفارش"], input[placeholder*="کد رهگیری"]').first();
|
||||||
|
await expect(trackingInput).toBeVisible({ timeout: 15000 });
|
||||||
|
|
||||||
|
const trackBtn = page.getByRole('button', { name: /رهگیری|پیگیری|استعلام/i }).or(
|
||||||
|
page.locator('button').filter({ hasText: /رهگیری|پیگیری/i })
|
||||||
|
).first();
|
||||||
|
await expect(trackBtn).toBeVisible();
|
||||||
|
|
||||||
|
// 3. تست Edge Case: ورود کد پیگیری نامعتبر
|
||||||
|
await trackingInput.fill('INVALID-TRACK-99999');
|
||||||
|
await trackBtn.click();
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
|
||||||
|
// 4. تست ورود کد رهگیری ساختگی یا بررسی ساختار تایملاین وضعیت
|
||||||
|
await trackingInput.fill('CN-10001');
|
||||||
|
await trackBtn.click();
|
||||||
|
|
||||||
|
// 5. بررسی تایملاین مراحل تحویل (ثبت سفارش، آمادهسازی، ارسال)
|
||||||
|
const timelineStep = page.locator('div, h4, span').filter({ hasText: /ثبت سفارش|آمادهسازی|تحویل|وضعیت/i }).first();
|
||||||
|
await expect(timelineStep).toBeVisible({ timeout: 10000 });
|
||||||
|
});
|
||||||
|
});
|
||||||
48
tests/e2e/storefront/shop-filter-search.spec.ts
Normal file
48
tests/e2e/storefront/shop-filter-search.spec.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { test, expect } from '../../fixtures';
|
||||||
|
|
||||||
|
test.describe('سناریوی جستجو، فیلترها و مرتبسازی در فروشگاه (Shop Filter, Search & Edge Cases)', () => {
|
||||||
|
test('جستجوی محصول، فیلتر بر اساس علائم و دستهها، و مدیریت حالت بدون نتیجه', async ({ page }) => {
|
||||||
|
// 1. ورود به صفحه فروشگاه
|
||||||
|
await page.goto('/shop', { waitUntil: 'domcontentloaded' });
|
||||||
|
await expect(page).toHaveURL(/\/shop/);
|
||||||
|
|
||||||
|
// 2. تست باکس جستجو
|
||||||
|
const searchInput = page.locator('input[placeholder*="جستجو"], input[type="search"]').first();
|
||||||
|
if (await searchInput.isVisible()) {
|
||||||
|
// جستجوی یک کلمه کلیدی موجود
|
||||||
|
await searchInput.fill('مفاصل');
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
|
||||||
|
// پاک کردن و جستجوی عبارت ناموجود برای تست Edge Case (بدون نتیجه)
|
||||||
|
await searchInput.fill('عبارت_ناموجود_تستی_۱۲۳۴۵۶');
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
|
||||||
|
// بررسی وضعیت خالی / عدم یافت محصول یا پاک کردن فیلتر
|
||||||
|
const clearSearchOrEmpty = page.locator('text=یافت نشد').or(
|
||||||
|
page.locator('button').filter({ hasText: /پاک کردن|نمایش همه/i })
|
||||||
|
).or(searchInput);
|
||||||
|
await expect(clearSearchOrEmpty.first()).toBeVisible();
|
||||||
|
|
||||||
|
// بازگردانی جستجو به حالت عادی
|
||||||
|
await searchInput.fill('');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. تست دکمههای فیلتر دستهبندی
|
||||||
|
const categoryTabs = page.locator('button, a').filter({ hasText: /مفاصل|مکمل|پت|ویتامین/i }).first();
|
||||||
|
if (await categoryTabs.isVisible()) {
|
||||||
|
await categoryTabs.click();
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. تست کلیک روی تگ علائم یا اندیکاسیون (Symptom Badges)
|
||||||
|
const symptomBadge = page.locator('a[href*="symptom="], button[data-symptom]').first();
|
||||||
|
if (await symptomBadge.isVisible()) {
|
||||||
|
await symptomBadge.click();
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. بررسی نمایش حداقل یک کارت محصول یا ساختار گرید کاتالوگ
|
||||||
|
const productGrid = page.locator('a[href^="/shop/"], div[data-product-card]').first();
|
||||||
|
await expect(productGrid).toBeVisible({ timeout: 15000 });
|
||||||
|
});
|
||||||
|
});
|
||||||
22
tsconfig.json
Normal file
22
tsconfig.json
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "NodeNext",
|
||||||
|
"moduleResolution": "NodeNext",
|
||||||
|
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": false,
|
||||||
|
"noEmit": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"types": ["node", "@playwright/test"]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"playwright.config.ts",
|
||||||
|
"tests/**/*.ts"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"backend",
|
||||||
|
"frontend"
|
||||||
|
]
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user