الاختبار الشامل باستخدام Playwright و Next.js: من الصفر إلى خط أنابيب CI

معظم الفرق تُطلق الشفرة بدون شبكة أمان. يختبرون يدويًا، ويعقدون أصابعهم عند النشر، ويكتشفون الانحدارات عندما يبلّغ عنها المستخدمون. الاختبار الشامل يُغيّر ذلك — يتيح لك محاكاة سلوك المستخدم الحقيقي والتقاط الأعطال قبل وصولها إلى بيئة الإنتاج.
Playwright هو إطار الاختبار الذي تصدّر المشهد في 2026. تديره Microsoft، ويدعم Chromium وFirefox وWebKit بواجهة برمجة واحدة، ويتعامل مع أنماط الويب الحديثة تلقائيًا، ولديه دعم TypeScript من الدرجة الأولى.
في هذا الدليل، سنبني إعداد اختبار شامل متكامل لتطبيق Next.js — من تثبيت Playwright إلى تشغيل الاختبارات في CI مع GitHub Actions.
لماذا Playwright بدلاً من Cypress؟ يدعم Playwright سيناريوهات علامات التبويب المتعددة، وiframes، وتنزيل الملفات، وسياقات المتصفح الأصلية. أسرع لأنه يُشغّل الاختبارات بالتوازي افتراضيًا، وآلية الانتظار التلقائي تجعل الاختبارات أكثر استقرارًا بطبيعتها.
ما ستتعلمه
بنهاية هذا الدليل، ستتمكن من:
- إعداد Playwright في مشروع Next.js مع TypeScript
- كتابة اختبارات شاملة مرنة باستخدام المحدّدات والانتظار التلقائي
- تنظيم الاختبارات بنمط Page Object Model
- التقاط لقطات انحدار بصري
- تشغيل تدقيقات إمكانية الوصول باستخدام
@axe-core/playwright - دمج كل شيء في خط أنابيب CI مع GitHub Actions
- تصحيح الاختبارات الفاشلة باستخدام التتبعات ولقطات الشاشة وتسجيلات الفيديو
المتطلبات المسبقة
قبل البدء، تأكد من توفر:
- Node.js إصدار 18+ مُثبّت
- مشروع Next.js (App Router أو Pages Router — كلاهما يعمل)
- معرفة أساسية بـ TypeScript و React
- مستودع GitHub (لقسم CI)
- محرر شفرة (يُنصح بـ VS Code — Playwright لديه إضافة ممتازة لـ VS Code)
إعداد المشروع
سنعمل مع تطبيق Next.js بسيط يحتوي على صفحة رئيسية ونموذج اتصال ولوحة تحكم. إذا لم يكن لديك مشروع، أنشئ واحدًا:
npx create-next-app@latest my-app --typescript --app --tailwind
cd my-appالخطوة 1: تثبيت Playwright
يأتي Playwright بأمر init خاص يُهيكل كل ما تحتاجه:
npm init playwright@latestعند السؤال، اختر ما يلي:
- أين تضع اختباراتك؟ →
e2e - إضافة سير عمل GitHub Actions؟ → نعم
- تثبيت متصفحات Playwright؟ → نعم
هذا يُثبّت @playwright/test، ويُنزّل ملفات المتصفحات، ويُنشئ ملفات الإعداد.
مشروعك الآن يحتوي على:
my-app/
├── e2e/
│ └── example.spec.ts # اختبار نموذجي
├── playwright.config.ts # الإعداد
├── .github/
│ └── workflows/
│ └── playwright.yml # سير عمل CI
└── ...
الخطوة 2: إعداد Playwright لـ Next.js
الإعداد المُولّد يحتاج تعديلات لـ Next.js. استبدل محتوى playwright.config.ts:
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './e2e',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: process.env.CI
? [['html'], ['github']]
: [['html'], ['list']],
use: {
baseURL: 'http://localhost:3000',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
video: 'on-first-retry',
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
{
name: 'mobile-chrome',
use: { ...devices['Pixel 7'] },
},
],
webServer: {
command: 'npm run dev',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
timeout: 120_000,
},
});القرارات الأساسية هنا:
webServerيُشغّل خادم Next.js التطويري قبل تشغيل الاختبارات ويُوقفه بعدها. في CI، يبدأ دائمًا من جديد؛ محليًا، يُعيد استخدام خادم موجود إن كان يعمل.trace: 'on-first-retry'يُسجّل تتبعًا كاملاً عند فشل الاختبار، مما يمنحك جدولاً زمنيًا للإجراءات وطلبات الشبكة ولقطات DOM.fullyParallel: trueيُشغّل ملفات الاختبار بالتوازي عبر العمّال لتحقيق السرعة.- أربعة مشاريع تختبر عبر Chrome وFirefox وSafari وChrome للهواتف.
الخطوة 3: كتابة أول اختبار لك
احذف الاختبار النموذجي وأنشئ e2e/home.spec.ts:
import { test, expect } from '@playwright/test';
test.describe('Home Page', () => {
test('should display the hero section', async ({ page }) => {
await page.goto('/');
// التحقق من تحميل الصفحة
await expect(page).toHaveTitle(/My App/);
// التحقق من ظهور عنوان القسم الرئيسي
const heading = page.getByRole('heading', { level: 1 });
await expect(heading).toBeVisible();
});
test('should navigate to the about page', async ({ page }) => {
await page.goto('/');
// النقر على رابط التنقل
await page.getByRole('link', { name: /about/i }).click();
// التحقق من تغيّر عنوان URL
await expect(page).toHaveURL(/\/about/);
});
test('should be responsive on mobile', async ({ page }) => {
await page.setViewportSize({ width: 375, height: 667 });
await page.goto('/');
// زر القائمة في الهاتف يجب أن يكون مرئيًا
const menuButton = page.getByRole('button', { name: /menu/i });
await expect(menuButton).toBeVisible();
});
});شغّل الاختبار:
npx playwright testسترى نتائج لجميع مشاريع المتصفحات الأربعة. لتشغيل Chromium فقط:
npx playwright test --project=chromiumلمشاهدة الاختبار بمتصفح مرئي:
npx playwright test --headed --project=chromiumاستخدم محدّدات getByRole. محدّدات Playwright المبنية على الأدوار (getByRole، getByLabel، getByPlaceholder، getByText) أكثر مرونة من محدّدات CSS. تتطابق مع طريقة تفاعل المستخدمين وتقنيات المساعدة مع واجهتك، وتصمد أمام إعادة الهيكلة التي تُغيّر أسماء الأصناف أو بنية DOM.
الخطوة 4: اختبار النماذج وتفاعلات المستخدم
النماذج هي حيث تختبئ معظم الأخطاء. أنشئ e2e/contact-form.spec.ts:
import { test, expect } from '@playwright/test';
test.describe('Contact Form', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/contact');
});
test('should submit the form successfully', async ({ page }) => {
// ملء حقول النموذج
await page.getByLabel('Name').fill('Jane Developer');
await page.getByLabel('Email').fill('jane@example.com');
await page.getByLabel('Message').fill('Hello, I have a question about your services.');
// إرسال النموذج
await page.getByRole('button', { name: /send|submit/i }).click();
// التحقق من رسالة النجاح
await expect(
page.getByText(/thank you|message sent/i)
).toBeVisible();
});
test('should show validation errors for empty fields', async ({ page }) => {
// الإرسال بدون ملء أي شيء
await page.getByRole('button', { name: /send|submit/i }).click();
// التحقق من رسائل التحقق
await expect(page.getByText(/name is required/i)).toBeVisible();
await expect(page.getByText(/email is required/i)).toBeVisible();
});
test('should validate email format', async ({ page }) => {
await page.getByLabel('Name').fill('Jane');
await page.getByLabel('Email').fill('not-an-email');
await page.getByLabel('Message').fill('Test message');
await page.getByRole('button', { name: /send|submit/i }).click();
await expect(
page.getByText(/valid email/i)
).toBeVisible();
});
});الخطوة 5: نمط Page Object Model
مع نمو مجموعة اختباراتك، ستلاحظ أنماطًا متكررة — التنقل إلى صفحة، ملء النماذج، فحص عناصر محددة. نمط Page Object Model (POM) يُجرّد هذه إلى أصناف قابلة لإعادة الاستخدام.
أنشئ e2e/pages/contact-page.ts:
import { type Locator, type Page, expect } from '@playwright/test';
export class ContactPage {
readonly page: Page;
readonly nameInput: Locator;
readonly emailInput: Locator;
readonly messageInput: Locator;
readonly submitButton: Locator;
readonly successMessage: Locator;
constructor(page: Page) {
this.page = page;
this.nameInput = page.getByLabel('Name');
this.emailInput = page.getByLabel('Email');
this.messageInput = page.getByLabel('Message');
this.submitButton = page.getByRole('button', { name: /send|submit/i });
this.successMessage = page.getByText(/thank you|message sent/i);
}
async goto() {
await this.page.goto('/contact');
}
async fillForm(name: string, email: string, message: string) {
await this.nameInput.fill(name);
await this.emailInput.fill(email);
await this.messageInput.fill(message);
}
async submit() {
await this.submitButton.click();
}
async expectSuccess() {
await expect(this.successMessage).toBeVisible();
}
}الآن اختباراتك تصبح أنظف بكثير:
import { test } from '@playwright/test';
import { ContactPage } from './pages/contact-page';
test.describe('Contact Form', () => {
test('should submit successfully', async ({ page }) => {
const contactPage = new ContactPage(page);
await contactPage.goto();
await contactPage.fillForm('Jane', 'jane@example.com', 'Hello!');
await contactPage.submit();
await contactPage.expectSuccess();
});
});نمط POM يُؤتي ثماره بسرعة:
- إذا تغيّرت واجهة النموذج، تُحدّث ملفًا واحدًا بدلاً من كل اختبار
- الاختبارات تُقرأ كقصص مستخدم: "اذهب للصفحة، املأ النموذج، أرسل، توقّع نجاح"
- أعضاء الفريق الجدد يمكنهم كتابة اختبارات بدون تعلّم بنية DOM
الخطوة 6: محاكاة استدعاءات API
الاختبارات الشاملة يجب أن تكون حتمية. إذا كان نموذجك يستدعي API، لا تريد فشل الاختبارات لأن الخادم الخلفي معطّل. يتيح Playwright اعتراض طلبات الشبكة:
import { test, expect } from '@playwright/test';
import { ContactPage } from './pages/contact-page';
test('should handle API errors gracefully', async ({ page }) => {
// اعتراض استدعاء API وإرجاع خطأ
await page.route('**/api/contact', (route) => {
route.fulfill({
status: 500,
contentType: 'application/json',
body: JSON.stringify({ error: 'Internal server error' }),
});
});
const contactPage = new ContactPage(page);
await contactPage.goto();
await contactPage.fillForm('Jane', 'jane@example.com', 'Hello!');
await contactPage.submit();
// التحقق من عرض الخطأ للمستخدم
await expect(
page.getByText(/something went wrong|try again/i)
).toBeVisible();
});
test('should show loading state during submission', async ({ page }) => {
// تأخير استجابة API لملاحظة حالة التحميل
await page.route('**/api/contact', async (route) => {
await new Promise((resolve) => setTimeout(resolve, 2000));
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ success: true }),
});
});
const contactPage = new ContactPage(page);
await contactPage.goto();
await contactPage.fillForm('Jane', 'jane@example.com', 'Hello!');
await contactPage.submit();
// الزر يجب أن يُظهر مؤشر تحميل
await expect(
page.getByRole('button', { name: /sending|loading/i })
).toBeVisible();
});الخطوة 7: اختبار الانحدار البصري
اختبارات الانحدار البصري تلتقط تغييرات واجهة المستخدم غير المقصودة بمقارنة لقطات الشاشة مع خط أساسي. هذه الميزة مدمجة في Playwright:
import { test, expect } from '@playwright/test';
test.describe('Visual Regression', () => {
test('home page matches snapshot', async ({ page }) => {
await page.goto('/');
// الانتظار حتى تستقر الخطوط والصور والرسوم المتحركة
await page.waitForLoadState('networkidle');
await expect(page).toHaveScreenshot('home-page.png', {
maxDiffPixelRatio: 0.01,
});
});
test('contact form matches snapshot', async ({ page }) => {
await page.goto('/contact');
await page.waitForLoadState('networkidle');
await expect(page).toHaveScreenshot('contact-form.png', {
maxDiffPixelRatio: 0.01,
});
});
test('dashboard cards layout', async ({ page }) => {
await page.goto('/dashboard');
await page.waitForLoadState('networkidle');
// لقطة شاشة لعنصر محدد
const cardsSection = page.getByTestId('dashboard-cards');
await expect(cardsSection).toHaveScreenshot('dashboard-cards.png');
});
});أول مرة تُشغّل هذه الاختبارات، ينشئ Playwright لقطات أساسية في e2e/__screenshots__/. في المرات التالية، يُقارن مع الخط الأساسي ويفشل إذا تجاوز الفرق الحد المسموح.
لتحديث الخطوط الأساسية بعد تغييرات مقصودة:
npx playwright test --update-snapshotsالاختبارات البصرية خاصة بالمشروع. لقطات الشاشة تختلف بين أنظمة التشغيل بسبب عرض الخطوط. شغّل --update-snapshots في بيئة CI (Linux) وأودع تلك الخطوط الأساسية. لا تستخدم لقطات macOS المولّدة محليًا كخطوط أساسية لـ CI على Linux.
الخطوة 8: اختبار إمكانية الوصول مع axe-core
إمكانية الوصول ليست اختيارية — إنها متطلب قانوني في العديد من البلدان والشيء الصحيح الذي يجب فعله. حزمة @axe-core/playwright تدمج محرك axe المعتمد في الصناعة مع اختبارات Playwright.
ثبّتها:
npm install -D @axe-core/playwrightأنشئ e2e/accessibility.spec.ts:
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test.describe('Accessibility', () => {
test('home page has no critical violations', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21aa'])
.analyze();
expect(results.violations).toEqual([]);
});
test('contact form is accessible', async ({ page }) => {
await page.goto('/contact');
const results = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa'])
.analyze();
// طباعة الانتهاكات لأغراض التصحيح
if (results.violations.length > 0) {
console.log('Accessibility violations:');
results.violations.forEach((v) => {
console.log(` [${v.impact}] ${v.id}: ${v.description}`);
v.nodes.forEach((n) => {
console.log(` - ${n.html}`);
});
});
}
expect(results.violations).toEqual([]);
});
test('navigation is keyboard accessible', async ({ page }) => {
await page.goto('/');
// التنقل بالضغط على Tab عبر شريط التنقل
await page.keyboard.press('Tab');
const firstFocused = await page.evaluate(() =>
document.activeElement?.tagName.toLowerCase()
);
expect(firstFocused).toBe('a');
// التحقق من أن التركيز مرئي (غير مخفي بـ CSS)
const focusedElement = page.locator(':focus');
await expect(focusedElement).toBeVisible();
await expect(focusedElement).toHaveCSS('outline-style', /(solid|auto)/);
});
});هذا يلتقط مشاكل إمكانية الوصول الواقعية:
- نص بديل مفقود في الصور
- تباين ألوان غير كافٍ
- حقول نماذج بدون تسميات
- خصائص ARIA مفقودة
- مصائد التنقل بلوحة المفاتيح
الخطوة 9: اختبار تدفقات المصادقة
إذا كان تطبيقك يحتوي على وظيفة تسجيل الدخول، ستريد اختبارها بدون تسجيل الدخول في كل اختبار. ميزة حالة التخزين في Playwright تتيح المصادقة مرة واحدة وإعادة استخدام الجلسة:
أنشئ ملف إعداد e2e/auth.setup.ts:
import { test as setup, expect } from '@playwright/test';
import path from 'node:path';
const authFile = path.join(__dirname, '../.playwright/.auth/user.json');
setup('authenticate', async ({ page }) => {
await page.goto('/login');
await page.getByLabel('Email').fill('test@example.com');
await page.getByLabel('Password').fill('test-password');
await page.getByRole('button', { name: /sign in/i }).click();
// الانتظار لإعادة التوجيه بعد تسجيل الدخول
await page.waitForURL('/dashboard');
// حفظ حالة المصادقة
await page.context().storageState({ path: authFile });
});حدّث playwright.config.ts لاستخدام هذا الإعداد:
export default defineConfig({
// ... الإعداد الحالي
projects: [
// مشروع الإعداد - يُشغّل أولاً
{ name: 'setup', testMatch: /.*\.setup\.ts/ },
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
storageState: '.playwright/.auth/user.json',
},
dependencies: ['setup'],
},
// ... مشاريع المتصفحات الأخرى بنفس storageState
],
});الآن جميع الاختبارات في مشروع chromium تبدأ بجلسة مصادقة — لا حاجة لخطوة تسجيل الدخول.
أضف .playwright/ إلى .gitignore:
.playwright/
الخطوة 10: تصحيح الاختبارات الفاشلة
الاختبارات ستفشل. إليك أدوات التصحيح:
وضع واجهة Playwright
أفضل تجربة تصحيح. يعرض كل إجراء وطلب شبكة ولقطة DOM في جدول زمني:
npx playwright test --uiعارض التتبع
عندما تفشل الاختبارات في CI، ملف التتبع يحتوي على كل ما تحتاجه:
npx playwright show-trace test-results/home-page-chromium/trace.zipعارض التتبع يعرض:
- جدول زمني لكل إجراء
- لقطات DOM في كل خطوة
- طلبات واستجابات الشبكة
- سجلات وحدة التحكم
- شريط فيلمي لتنفيذ الاختبار
توليد الشفرة
إذا لم تكن متأكدًا من المحدّدات التي تستخدمها، دع Playwright يولّدها:
npx playwright codegen localhost:3000هذا يفتح متصفحًا ومولّد شفرة. أثناء تفاعلك مع الصفحة، يكتب Playwright شفرة الاختبار لك.
إضافة VS Code
ثبّت إضافة Playwright Test for VS Code للحصول على:
- تشغيل اختبارات فردية من المحرر
- التنقل خطوة بخطوة مع نقاط توقف
- اختيار المحدّدات بالنقر على العناصر
- نتائج الاختبار المباشرة في مستكشف الاختبارات
الخطوة 11: تنظيم مجموعة اختبارات كبيرة
مع نمو مجموعة اختباراتك، الهيكلة مهمة. إليك تخطيطًا مُجربًا:
e2e/
├── fixtures/
│ └── test-data.ts # بيانات اختبار مشتركة
├── pages/
│ ├── home-page.ts # Page Object: الرئيسية
│ ├── contact-page.ts # Page Object: الاتصال
│ └── dashboard-page.ts # Page Object: لوحة التحكم
├── specs/
│ ├── home.spec.ts # اختبارات الصفحة الرئيسية
│ ├── contact-form.spec.ts # اختبارات نموذج الاتصال
│ ├── dashboard.spec.ts # اختبارات لوحة التحكم
│ ├── accessibility.spec.ts # اختبارات إمكانية الوصول
│ └── visual.spec.ts # اختبارات الانحدار البصري
├── auth.setup.ts # إعداد المصادقة
└── global-setup.ts # إعداد شامل (تهيئة قاعدة البيانات، إلخ.)
التركيبات المخصصة
نظام التركيبات في Playwright يتيح إنشاء مساعدات اختبار قابلة لإعادة الاستخدام:
// e2e/fixtures/test-data.ts
import { test as base } from '@playwright/test';
import { ContactPage } from '../pages/contact-page';
type Fixtures = {
contactPage: ContactPage;
};
export const test = base.extend<Fixtures>({
contactPage: async ({ page }, use) => {
const contactPage = new ContactPage(page);
await contactPage.goto();
await use(contactPage);
},
});
export { expect } from '@playwright/test';الآن يمكن للاختبارات طلب التركيبة مباشرة:
import { test, expect } from '../fixtures/test-data';
test('should submit form', async ({ contactPage }) => {
await contactPage.fillForm('Jane', 'jane@example.com', 'Hello!');
await contactPage.submit();
await contactPage.expectSuccess();
});الخطوة 12: خط أنابيب CI مع GitHub Actions
القطعة الأخيرة هي تشغيل الاختبارات تلقائيًا مع كل دفع وطلب سحب. أنشئ .github/workflows/playwright.yml:
name: Playwright Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
timeout-minutes: 15
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Install Playwright browsers
run: npx playwright install --with-deps
- name: Build the application
run: npm run build
- name: Run Playwright tests
run: npx playwright test
env:
CI: true
- name: Upload test report
uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: playwright-report
path: playwright-report/
retention-days: 30
- name: Upload test results
uses: actions/upload-artifact@v4
if: failure()
with:
name: test-results
path: test-results/
retention-days: 7التفاصيل الأساسية:
npm run buildقبل الاختبار يضمن أن تطبيقك يُبنى بشكل صحيح وأن الاختبارات تعمل على بناء الإنتاج — استخدمcommand: 'npm run start'في إعداد Playwright لـ CI لاختبار خادم الإنتاج بدلاً من خادم التطوير.- المنتجات تُرفع عند الفشل حتى تتمكن من تنزيل التتبعات ولقطات الشاشة ومقاطع الفيديو للتصحيح.
timeout-minutes: 15يمنع الاختبارات المتهوّرة من استهلاك دقائق CI.
إعداد Playwright خاص بـ CI
حدّث إعداد Playwright لاستخدام خادم الإنتاج في CI:
webServer: {
command: process.env.CI ? 'npm run start' : 'npm run dev',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
timeout: 120_000,
},استكشاف الأخطاء وإصلاحها
الاختبارات تنجح محليًا لكنها تفشل في CI
هذا يكاد يكون دائمًا بسبب مشاكل التوقيت أو اختلافات البيئة:
- استخدم
await page.waitForLoadState('networkidle')قبل التأكيدات التي تعتمد على محتوى ديناميكي - زِد المهل الزمنية لبيئات CI البطيئة:
test.setTimeout(60_000) - تحقق مما إذا كانت بيئة CI لديها التبعيات المطلوبة —
npx playwright install --with-depsيتعامل مع ذلك
الاختبارات غير المستقرة
عدم الاستقرار عادة يأتي من حالات السباق:
- لا تستخدم
page.waitForTimeout()— استخدم الانتظار التلقائي المدمج في Playwright بدلاً من ذلك - استخدم
await expect(locator).toBeVisible()بدلاً من الفحص الفوري - للرسوم المتحركة، استخدم
await page.waitForFunction()للانتظار حتى اكتمال الرسوم المتحركة
فشل اختبارات لقطات الشاشة مع اختلافات طفيفة
عرض الخطوط يختلف بين أنظمة التشغيل وحتى صور Docker:
- ولّد الخطوط الأساسية دائمًا في نفس البيئة التي تعمل فيها الاختبارات (CI)
- استخدم
maxDiffPixelRatio: 0.01للسماح باختلافات العرض دون البكسل - فكّر في إخفاء المحتوى الديناميكي مثل الطوابع الزمنية:
await expect(page).toHaveScreenshot('page.png', {
mask: [page.getByTestId('timestamp')],
});الخطوات التالية
لديك الآن إعداد اختبار شامل بمستوى إنتاجي. إليك طرقًا للمضي قدمًا:
- أضف اختبار API — تركيبة
requestفي Playwright تتيح اختبار نقاط نهاية API مباشرة بدون متصفح - نفّذ اختبار المكونات — اختبار المكونات التجريبي في Playwright يتيح اختبار مكونات React بشكل منفصل
- أعد تقسيم Playwright — وزّع الاختبارات عبر أجهزة CI متعددة لملاحظات أسرع
- أضف مقاييس الأداء — استخدم
page.metrics()لتتبع Core Web Vitals في اختباراتك - استكشف Playwright MCP — تكامل Model Context Protocol الجديد يُمكّن توليد الاختبارات بالذكاء الاصطناعي والاختبارات ذاتية الإصلاح
الخلاصة
الاختبار الشامل مع Playwright ليس فقط لالتقاط الأخطاء — بل لبناء الثقة. عندما يمر كل طلب سحب عبر مجموعة اختباراتك، تعلم أن التنقل يعمل، والنماذج تُرسل بشكل صحيح، والصفحات تُعرض بدون انحدارات بصرية، وتطبيقك متاح للجميع.
الإعداد يستغرق بضع ساعات. راحة البال تدوم إلى الأبد.
ابدأ بتدفقات المستخدم الحاسمة — تسجيل الدخول، التسجيل، الدفع — وتوسّع من هناك. نمط Page Object Model يحافظ على صيانة اختباراتك، والانحدار البصري يلتقط ما يُغفله البشر، وGitHub Actions يضمن عدم تسلّل أي شيء.
أنت المستقبلي (ومستخدموك) سيشكرونك.
ناقش مشروعك معنا
نحن هنا للمساعدة في احتياجات تطوير الويب الخاصة بك. حدد موعدًا لمناقشة مشروعك وكيف يمكننا مساعدتك.
دعنا نجد أفضل الحلول لاحتياجاتك.
مقالات ذات صلة

بناء مستودع أحادي جاهز للإنتاج باستخدام Turborepo و Next.js والحزم المشتركة
تعلّم كيفية بناء مستودع أحادي (Monorepo) جاهز للإنتاج باستخدام Turborepo مع تطبيقات Next.js ومكوّنات واجهة مستخدم مشتركة وحزمة إعدادات TypeScript وخطوط أنابيب CI/CD فعّالة. يغطي هذا الدليل الشامل إعداد مساحات العمل ومشاركة الحزم والتخزين المؤقت عن بُعد والنشر.

Vitest و React Testing Library مع Next.js 15: الدليل الشامل لاختبارات الوحدة في 2026
تعلم كيفية إعداد وإتقان Vitest مع React Testing Library في مشروع Next.js 15. يغطي هذا الدليل اختبار المكونات والـ hooks وServer Components ومسارات API والتكامل مع CI/CD.

بناء وكيل ذكاء اصطناعي مستقل باستخدام Agentic RAG و Next.js
تعلم كيف تبني وكيل ذكاء اصطناعي يقرر بشكل مستقل متى وكيف يسترجع المعلومات من قواعد البيانات المتجهية. دليل عملي شامل باستخدام Vercel AI SDK و Next.js مع أمثلة قابلة للتنفيذ.