From 3c325c412ca0bb00ec8a293926507c4d45a9ea30 Mon Sep 17 00:00:00 2001
From: parsa aghaei
Date: Tue, 25 Aug 2026 13:07:00 +0330
Subject: [PATCH] test(e2e): expand Playwright test coverage with shop filters,
tracking, cart operations and admin modules
---
test-coverage-map.md | 72 +++++++++++++
tests/e2e/admin/admin-b2b-submissions.spec.ts | 86 +++++++++++++++
tests/e2e/admin/admin-blogs-crud.spec.ts | 102 ++++++++++++++++++
tests/e2e/admin/admin-orders-flow.spec.ts | 94 ++++++++++++++++
tests/e2e/storefront/cart-operations.spec.ts | 36 +++++++
tests/e2e/storefront/checkout-flow.spec.ts | 2 +
tests/e2e/storefront/order-tracking.spec.ts | 31 ++++++
.../e2e/storefront/shop-filter-search.spec.ts | 48 +++++++++
tsconfig.json | 22 ++++
9 files changed, 493 insertions(+)
create mode 100644 test-coverage-map.md
create mode 100644 tests/e2e/admin/admin-b2b-submissions.spec.ts
create mode 100644 tests/e2e/admin/admin-blogs-crud.spec.ts
create mode 100644 tests/e2e/admin/admin-orders-flow.spec.ts
create mode 100644 tests/e2e/storefront/cart-operations.spec.ts
create mode 100644 tests/e2e/storefront/order-tracking.spec.ts
create mode 100644 tests/e2e/storefront/shop-filter-search.spec.ts
create mode 100644 tsconfig.json
diff --git a/test-coverage-map.md b/test-coverage-map.md
new file mode 100644
index 0000000..7d95a90
--- /dev/null
+++ b/test-coverage-map.md
@@ -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 و تماس.
diff --git a/tests/e2e/admin/admin-b2b-submissions.spec.ts b/tests/e2e/admin/admin-b2b-submissions.spec.ts
new file mode 100644
index 0000000..0d5e982
--- /dev/null
+++ b/tests/e2e/admin/admin-b2b-submissions.spec.ts
@@ -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 });
+ });
+});
diff --git a/tests/e2e/admin/admin-blogs-crud.spec.ts b/tests/e2e/admin/admin-blogs-crud.spec.ts
new file mode 100644
index 0000000..20744bc
--- /dev/null
+++ b/tests/e2e/admin/admin-blogs-crud.spec.ts
@@ -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');
+ }
+ });
+});
diff --git a/tests/e2e/admin/admin-orders-flow.spec.ts b/tests/e2e/admin/admin-orders-flow.spec.ts
new file mode 100644
index 0000000..e070ecb
--- /dev/null
+++ b/tests/e2e/admin/admin-orders-flow.spec.ts
@@ -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();
+ }
+ });
+});
diff --git a/tests/e2e/storefront/cart-operations.spec.ts b/tests/e2e/storefront/cart-operations.spec.ts
new file mode 100644
index 0000000..01103d5
--- /dev/null
+++ b/tests/e2e/storefront/cart-operations.spec.ts
@@ -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 });
+ });
+});
diff --git a/tests/e2e/storefront/checkout-flow.spec.ts b/tests/e2e/storefront/checkout-flow.spec.ts
index 00d954b..7ce7909 100644
--- a/tests/e2e/storefront/checkout-flow.spec.ts
+++ b/tests/e2e/storefront/checkout-flow.spec.ts
@@ -24,6 +24,7 @@ test.describe('سناریوی خرید و سبد خرید (Storefront Checkout F
const buyButtonOnCard = page.locator('button').filter({ hasText: /خرید/i }).first();
if (await buyButtonOnCard.isVisible()) {
await buyButtonOnCard.click();
+ await page.waitForTimeout(1000);
} else {
const href = await productCard.getAttribute('href');
if (href) {
@@ -38,6 +39,7 @@ test.describe('سناریوی خرید و سبد خرید (Storefront Checkout F
).first();
await expect(addToCartBtn).toBeVisible({ timeout: 15000 });
await addToCartBtn.click();
+ await page.waitForTimeout(1000);
}
// 5. رفتن به صفحه تسویه حساب (Checkout)
diff --git a/tests/e2e/storefront/order-tracking.spec.ts b/tests/e2e/storefront/order-tracking.spec.ts
new file mode 100644
index 0000000..ff8a7f5
--- /dev/null
+++ b/tests/e2e/storefront/order-tracking.spec.ts
@@ -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 });
+ });
+});
diff --git a/tests/e2e/storefront/shop-filter-search.spec.ts b/tests/e2e/storefront/shop-filter-search.spec.ts
new file mode 100644
index 0000000..ae82ac9
--- /dev/null
+++ b/tests/e2e/storefront/shop-filter-search.spec.ts
@@ -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 });
+ });
+});
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..02dfc92
--- /dev/null
+++ b/tsconfig.json
@@ -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"
+ ]
+}