ورقة مراجعة أوامر QAcart

مقال مجاني 5 دقيقة

مقدمة

خلصنا كل أوامر المكتبة. هذا الدرس مرجع مكتوب — مش درس جديد.

فتحه وانت تكتب، مش حتى تحفظه. وبعد لا تنتقل لـ Selenium بالقسم السابع، سترجعله كذلك — لأنه درس التحويل هناك يربط كل أمر هون بمقابله.

نصيحة
احفظ رابط هذا الدرس. الصفحة التي تفتحها كل يوم بأول شهر أنفع من أي درس تشوفه مرة.

الأهداف التعليمية

بنهاية هذا الدرس، ستكون قادراً على:

  • تلاقي أي أمر بثانية
  • تعرف نمط كل عائلة أوامر
  • تكتب اختبار كامل من الورقة هذه
  • تعرف الأخطاء الأربعة الشائعة

1. التنقّل

await page.goto('https://qacart-todo.herokuapp.com');
await page.goBack();
await page.goForward();
await page.reload();

const url   = page.url();
const title = await page.title();

2. البحث عن العناصر

بالترتيب من الأفضل للأقل:

page.getByRole('button', { name: 'إضافة' })       // 1. الدور والاسم
page.getByLabel('البريد الإلكتروني')               // 2. عنوان الحقل
page.getByPlaceholder('كود الخصم')                // 3. النص التوضيحي
page.getByTestId('cart-total')                    // 4. معرّف الاختبار
page.getByText('تمت الإضافة')                     // 5. النص
page.locator('#email')                            // 6. CSS
page.locator('xpath=//button[text()="حفظ"]')      // 7. XPath — آخر خيار

والفلترة:

page.locator('.card', { hasText: 'iPhone 15' })            // حاوية فيها نص
page.locator('.card:has(h3)')                              // حاوية فيها عنصر
page.locator('tr').filter({ hasText: 'ORD-4471' })         // فلترة
page.locator('li').nth(2)                                  // بالفهرس
page.locator('li').first()
page.locator('li').last()
page.locator('.card').locator('button')                    // تسلسل
نصيحة
getByRole أول خيار دايماً — لأنه يوصف العنصر كما المستخدم يشوفه. ولو انكسر، معناها التجربة تغيّرت فعلياً — وهذا بالضبط لا تريد اختبارك يلقطه.

3. التفاعل

await locator.click();
await locator.dblclick();
await locator.click({ button: 'right' });

await locator.fill('iPhone 15');            // بيمسح وبيكتب — الأفضل
await locator.clear();
await locator.press('Enter');
await locator.type('iPhone', { delay: 100 });   // حرف حرف

await locator.check();                      // checkbox / radio
await locator.uncheck();
await locator.selectOption('amman');        // قائمة منسدلة
await locator.selectOption({ label: 'عمان' });

await locator.hover();
await locator.focus();
await locator.setInputFiles('./id-card.png');    // رفع ملف
await locator.dragTo(target);
await locator.scrollIntoViewIfNeeded();
مهم
fill() مش type() للحقول العادية. fill تمسح وتكتب مرة واحدة وبتشعّل الأحداث صح. وtype تكتب حرف حرف — استخدمها لكن عندما تختبر بحث فوري (autocomplete) أو تحقّق أثناء الكتابة.

4. القراءة

await locator.textContent();
await locator.innerText();
await locator.inputValue();              // للحقول
await locator.getAttribute('href');
await locator.isVisible();
await locator.isEnabled();
await locator.isChecked();
await locator.count();
await locator.all();
تحذير
textContent مقابل innerText: الأولى بترجّع كل النص حتى المخفي، والثانية بترجّع المرئي بس. لو تفحص ما المستخدم يشوفه، استخدم innerText.

5. التحقق — الأهم

// ✅ مرر الـ Locator — تعيد المحاولة تلقائياً
await expect(locator).toBeVisible();
await expect(locator).toBeHidden();
await expect(locator).toBeEnabled();
await expect(locator).toBeDisabled();
await expect(locator).toBeChecked();
await expect(locator).toHaveText('1350');
await expect(locator).toContainText('1350');
await expect(locator).toHaveValue('ahmad@test.com');
await expect(locator).toHaveCount(3);
await expect(locator).toHaveAttribute('href', '/cart');
await expect(locator).toHaveCSS('background-color', 'rgb(255, 0, 0)');
await expect(page).toHaveURL(/.*\/dashboard/);
await expect(page).toHaveTitle(/السلة/);

// مرن — بيكمّل ويجمع الأخطاء
await expect.soft(locator).toHaveText('1350');

// مع رسالة
await expect(locator, 'الإجمالي بعد خصم 10%').toHaveText('1350');

// عكس
await expect(locator).not.toBeVisible();
مهم
القاعدة الذهبية: مرّر الـ Locator للـ expect، مش القيمة. javascript // ❌ تأخذ القيمة الآن — لو السيرفر ما رد بعد، فشل const total = await page.getByTestId('total').textContent(); expect(total).toBe('1350'); // ✅ تعيد المحاولة لحد لا تنجح await expect(page.getByTestId('total')).toHaveText('1350');

6. الانتظار

// ✅ الأفضل — الـ Assertion هي الانتظار
await expect(locator).toBeVisible();

// انتظار صريح عندما تحتاجه
await locator.waitFor({ state: 'visible' });
await locator.waitFor({ state: 'hidden' });
await page.waitForURL('**/dashboard');
await page.waitForResponse(res => res.url().includes('/api/todos'));

// ❌ ممنوع
await page.waitForTimeout(2000);
تحذير
waitForTimeout ممنوعة بمشروع نظيف. بتضيّع وقت عندما السيرفر سريع، وبتفشل عندما يتأخر، وتخفي مشاكل أداء حقيقية.

7. الـ API والمحاكاة

// طلب API مباشر — للتجهيز
const response = await request.post('/api/todos', {
  headers: { Authorization: `Bearer ${token}` },
  data: { item: 'اقرأ الدرس', isCompleted: false }
});
expect(response.status()).toBe(201);
const body = await response.json();

// محاكاة رد
await page.route('**/api/todos', route =>
  route.fulfill({ status: 200, body: JSON.stringify([]) })
);

// محاكاة خطأ
await page.route('**/api/todos', route => route.fulfill({ status: 500 }));

// مراقبة الطلبات
page.on('request', req => console.log(req.method(), req.url()));

8. أدوات مساعدة

await page.screenshot({ path: 'shot.png' });
await locator.screenshot({ path: 'element.png' });
await page.evaluate(() => localStorage.setItem('token', 'abc'));
await context.addCookies([{ name: 'session', value: 'abc', url: 'https://qacart.com' }]);

الأخطاء الأربعة الشائعة

الخطأ العرض الحل
نسيت await الاختبار يمر أخضر دايماً await قبل كل عملية
قرأت القيمة بدل الـ Locator فشل عشوائي مرّر الـ Locator لـ expect
type() بدل fill() بطيء وأحياناً ناقص fill()
waitForTimeout بطيء ومتذبذب expect أو waitFor
تحذير
الأول أخطرهم بمراتب. expect(locator).toHaveText('x') بدون await بترجّع Promise ولا أحد بينتظرها — والاختبار يمر أخضر مهما كانت النتيجة. فعّل قاعدة no-floating-promises بـ ESLint وهي تلقطه إلك. وإذا اختبار عندك ما فشل ولا مرة أبداً — افحص الـ await أول شيء.

قالب اختبار كامل

test('applying a valid coupon reduces the total by ten percent', async ({ page }) => {
// 1. تجهيز
  await page.goto('/cart');
  await expect(page.getByTestId('cart-item')).toHaveCount(1);

// 2. الفعل
  await page.getByTestId('coupon-input').fill('SAVE10');
  await page.getByTestId('coupon-apply').click();

// 3. التحقق
  await expect.soft(page.getByTestId('cart-subtotal'), 'المجموع الفرعي').toHaveText('1500');
  await expect.soft(page.getByTestId('cart-discount'), 'الخصم').toHaveText('150');
  await expect.soft(page.getByTestId('cart-total'), 'الإجمالي').toHaveText('1350');
});

خلاصة الدرس

  1. هذا مرجع مش درس — افتحه وانت تكتب، واحفظ رابطه.

  2. ترتيب المحددات: getByRole أول، XPath آخر.

  3. fill() للحقول العادية، type() لكن لاختبار البحث الفوري.

  4. textContent بترجّع المخفي كذلك، innerText المرئي بس.

  5. مرّر الـ Locator للـ expect مش القيمة — أهم قاعدة بالورقة كلها.

  6. expect هي الانتظار — لا تحتاج انتظار صريح بمعظم الحالات.

  7. waitForTimeout ممنوعة — وتخفي مشاكل أداء حقيقية.

  8. page.route للمحاكاة — أخطاء وحالات فاضية وتأخير.

  9. نسيان await أخطر خطأ — الاختبار يمر أخضر مهما صار.

  10. واختبار ما فشل ولا مرة أبداً = افحص الـ await.


وبهيك خلصنا قسم أساسيات مكتبة QAcart! صار عندك كل الأوامر، وتعرف تكتب اختبار كامل.

في القسم الجاي: تطبيقات عملية وأفضل الممارسات — هون بتتعلّم مش بس كيف تكتب اختبار، بس كيف تكتب اختبار جيد.

يلا، نلتقي بالقسم الجاي!