تست بلاک‌های وردپرس با Jest و Playwright یک ضرورت مهندسی است که از فروپاشی تجربه ویرایش محتوا در محیط Production جلوگیری می‌کند. Jest به‌عنوان یک Test Runner (اجراکننده تست) واحد، منطق JavaScript بلاک را در محیط شبیه‌سازی‌شده Node.js بررسی می‌کند، در حالی که Playwright با اتوماسیون مرورگر واقعی، تعامل کاربر با بلاک در ویرایشگر گوتنبرگ را شبیه‌سازی می‌نماید. بدون این دو لایه تست، هر تغییر در ساختار بلاک یا وابستگی‌ها می‌تواند بی‌صدا بخش‌هایی از سایت را از کار بیندازد. این مقاله چارچوب عملی راه‌اندازی، نوشتن و نگهداری تست برای بلاک‌های سفارشی وردپرس را ارائه می‌دهد. از تنظیمات wp-env و @wordpress/scripts تا تست‌های Snapshot، Mock، E2E و CI/CD.

اولین باری که یک بلاک سفارشی گوتنبرگ را بدون هیچ تستی روی یک سایت Production مستقر کردم، دو هفته بعد، پس از یک به‌روزرسانی جزئی در پکیج @wordpress/block-editor، ویرایشگر آن سایت کامل از کار افتاد. کنسول مرورگر پر از خطاهای TypeError: Cannot read property 'blocks' of undefined بود و تیم محتوا نمی‌توانست حتی یک نوشته جدید ذخیره کند. آن تجربه، هزینه واقعی نداشتن تست را برای همیشه در ذهنم حک کرد.

چرا تست بلاک‌های وردپرس ضروری است؟

بلاک‌های گوتنبرگ (Gutenberg Blocks) تنها قطعه کد ساده نیستند. هر بلاک یک اپلیکیشن کوچک React است که در سه محیط مختلف اجرا می‌شود: ویرایشگر (Editor)، فرانت‌اند (Front-end)، و ذخیره‌ساز (Serializer). این سه محیط، هرکدام چرخه حیات متفاوتی دارند و یک بلاک که در ویرایشگر کار می‌کند، ممکن است در فرانت‌اند ناقص رندر شود یا در Serializer داده‌های نادرستی تولید کند. اگر با آموزش ساخت بلاک سفارشی گوتنبرگ آشنا باشید، می‌دانید که پیچیدگی این چرخه سه‌گانه، تنها با تست قابل مدیریت است.

فرض کنید یک بلاک نمایش قیمت محصول را می‌سازید. در ویرایشگر، کاربر قیمت را وارد می‌کند. در فرانت‌اند، قیمت باید با فرمت صحیح و واحد پول نمایش داده شود. در Serializer، قیمت باید به‌صورت Attribute معتبر ذخیره شود تا در ویرایش‌های بعدی قابل بازیابی باشد. اگر فقط یکی از این سه لایه نادرست باشد، تجربه کاربری به‌طور کامل مختل می‌شود.

«یک بلاک بدون تست، شبیه یک پل بدون بازرسی فنی است: ممکن است سال‌ها کار کند، اما یک روز، بدون هشدار، فرو می‌ریزد.»

مشکل دوم، Dependency Drift (انحراف وابستگی) است. بلاک‌های گوتنبرگ به ده‌ها پکیج npm وابسته هستند: @wordpress/blocks، @wordpress/block-editor، @wordpress/components، @wordpress/data، @wordpress/i18n و ده‌ها پکیج دیگر. هر به‌روزرسانی در این پکیج‌ها می‌تواند رفتار بلاک را تغییر دهد. بدون تست، این تغییرات تا زمانی که در Production فاجعه ایجاد نکنند، کشف نمی‌شوند.

مشکل سوم، Regression (پس‌رفت) است. در پروژه‌های تیمی، هر توسعه‌دهنده ممکن است بخشی از منطق بلاک را تغییر دهد و بدون اینکه بداند، رفتار بخش دیگری را خراب کند. تست‌ها به‌عنوان یک شبکه ایمنی عمل می‌کنند: اگر تغییری باعث پس‌رفت شود، در محیط CI/CD قبل از Merge کشف می‌شود. اگر با تست و دیباگ پروژه‌های توسعه وردپرس آشنا شده باشید، این موضوع را به‌عنوان یک اصل بنیادین مهندسی می‌شناسید.

مشکل چهارم، Backward Compatibility است. بلاک‌های گوتنبرگ نه فقط باید در نسخه فعلی وردپرس کار کنند، بلکه باید با نسخه‌های قبلی نیز سازگار باشند. هر بلاک می‌تواند چندین deprecated داشته باشد که رفتار نسخه‌های قبلی را شبیه‌سازی می‌کنند. تست این Deprecationها بدون اتوماسیون تقریباً غیرممکن است.

Jest و Playwright: دو لایه مکمل تست

Jest و Playwright دو ابزار متفاوت با دو هدف متفاوت هستند. Jest یک Test Runner است که برای تست منطق JavaScript در محیط Node.js طراحی شده. Playwright یک Browser Automation Framework است که مرورگر واقعی را کنترل می‌کند و تعامل کاربر را شبیه‌سازی می‌نماید. ترکیب این دو، یک هرم تست (Testing Pyramid) کامل می‌سازد.

معیار Jest Playwright
محیط اجرا Node.js (بدون مرورگر) مرورگر واقعی (Chromium, Firefox, WebKit)
سرعت بسیار سریع (میلی‌ثانیه) کندتر (ثانیه)
نوع تست Unit، Integration، Snapshot End-to-End، Visual Regression
پوشش منطق بلاک، Attributeها، Deprecated تعامل کاربر، رندر نهایی، ذخیره محتوا
هزینه نگهداری پایین متوسط تا بالا
زمان اجرا در CI چند ثانیه چند دقیقه

Jest برای تست‌هایی که به مرورگر نیازی ندارند ایده‌آل است: تست توابع Helper، تست اعتبارسنجی Attributeها، تست منطق تبدیل داده، و تست Deprecated Blockها. Playwright برای تست‌هایی که باید مرورگر واقعی را کنترل کنند: تست درج بلاک در ویرایشگر، تست تغییر تنظیمات، تست ذخیره نوشته، و تست رندر فرانت‌اند.

«هر تستی که می‌تواند در Jest نوشته شود، نباید در Playwright نوشته شود. Playwright گران‌تر است، کندتر است، و شکننده‌تر. Jest را به‌عنوان خط اول دفاع و Playwright را به‌عنوان خط آخر در نظر بگیرید.»

این اصل، به Testing Trophy معروف است که توسط Kent C. Dodds مطرح شد: بیشترین تست باید در سطح Integration باشد (نه Unit، نه E2E)، و E2E فقط برای Critical Pathها نوشته شود. در بلاک‌های وردپرس، این یعنی تست‌های Jest برای منطق و Attributeها، تست‌های Playwright برای Workflowهای کاربر.

راه‌اندازی محیط تست با wp-env و @wordpress/scripts

وردپرس یک اکوسیستم رسمی برای تست بلاک‌ها فراهم می‌کند که شامل دو ابزار اصلی است: @wordpress/env برای راه‌اندازی محیط توسعه و @wordpress/scripts برای Build و Test. این دو ابزار، استاندارد رسمی تیم Gutenberg هستند.

نصب @wordpress/env

ابزار wp-env یک محیط وردپرس کامل با Docker راه‌اندازی می‌کند:

npm install --save-dev @wordpress/env
npx wp-env start

پس از اجرا، دو محیط ایجاد می‌شود: یک محیط Development در http://localhost:8888 و یک محیط Test در http://localhost:8889. محیط Test برای اجرای تست‌های Playwright استفاده می‌شود. اگر با توسعه وردپرس با محیط لوکال آشنا باشید، این ابزار را به‌عنوان نسخه رسمی و استاندارد آن می‌شناسید.

پیکربندی @wordpress/scripts

پکیج @wordpress/scripts تمام تنظیمات Webpack، Babel، ESLint و Jest را به‌صورت از پیش‌پیکربندی‌شده فراهم می‌کند:

npm install --save-dev @wordpress/scripts

سپس در فایل package.json اسکریپت‌های تست تعریف می‌شوند:

{
  "scripts": {
    "test:unit": "wp-scripts test-unit-js",
    "test:e2e": "wp-scripts test-playwright",
    "test:unit:watch": "wp-scripts test-unit-js --watch"
  }
}

پکیج @wordpress/scripts به‌طور خودکار فایل‌های .test.js و .spec.js در پوشه src/ را شناسایی می‌کند و تنظیمات Jest را بدون نیاز به پیکربندی دستی اعمال می‌نماید. این ابزار، بخش عمده‌ای از پیچیدگی راه‌اندازی تست را حذف می‌کند و به توسعه‌دهنده اجازه می‌دهد روی منطق تست تمرکز کند.

ساختار پوشه پیشنهادی

ساختار استاندارد برای یک پروژه بلاک با تست:

my-block/
├── src/
│   ├── index.js
│   ├── edit.js
│   ├── save.js
│   ├── utils.js
│   └── __tests__/
│       ├── utils.test.js
│       └── save.test.js
├── tests/
│   └── e2e/
│       └── block.spec.js
├── package.json
└── playwright.config.js

این ساختار، جداسازی تست‌های واحد و E2E را تضمین می‌کند و با ساختار فایل‌های یک افزونه استاندارد وردپرس هم‌خوانی دارد.

تست واحد با Jest: منطق بلاک

تست واحد در Jest برای بررسی منطق بلاک بدون نیاز به مرورگر استفاده می‌شود. این تست‌ها سریع، پایدار و ارزان هستند و باید بخش عمده هرم تست را تشکیل دهند.

تست توابع Helper

فرض کنید یک بلاک نمایش قیمت دارید که تابعی به‌نام formatPrice در فایل utils.js دارد:

// src/utils.js
export function formatPrice(amount, currency = "USD") {
  if (typeof amount !== "number" || isNaN(amount)) {
    return "";
  }
  const formatter = new Intl.NumberFormat("en-US", {
    style: "currency",
    currency,
  });
  return formatter.format(amount);
}

تست این تابع در Jest:

// src/__tests__/utils.test.js
import { formatPrice } from "../utils";

describe("formatPrice", () => {
  it("should format USD correctly", () => {
    expect(formatPrice(1234.5, "USD")).toBe("$1,234.50");
  });

  it("should handle zero", () => {
    expect(formatPrice(0, "USD")).toBe("$0.00");
  });

  it("should return empty string for invalid input", () => {
    expect(formatPrice("abc")).toBe("");
    expect(formatPrice(null)).toBe("");
    expect(formatPrice(undefined)).toBe("");
  });

  it("should support EUR currency", () => {
    expect(formatPrice(100, "EUR")).toContain("100");
  });
});

این تست‌ها در کمتر از ۵۰ میلی‌ثانیه اجرا می‌شوند و تمام Edge Caseها را پوشش می‌دهند. اگر تابع formatPrice تغییر کند و یکی از این رفتارها بشکند، تست در CI/CD شکست می‌خورد. اگر با اصول کدنویسی تمیز در پروژه‌های وردپرس آشنا باشید، جداسازی منطق در توابع Helper را به‌عنوان یک الگوی بنیادین می‌شناسید.

تست Attribute Validation

هر بلاک گوتنبرگ یک Schema از Attributeها دارد که در block.json یا در فراخوانی registerBlockType تعریف می‌شود. این Schema، اعتبارسنجی داده‌ها را تضمین می‌کند:

// src/index.js
import { registerBlockType } from "@wordpress/blocks";

registerBlockType("my-plugin/price-block", {
  attributes: {
    amount: { type: "number", default: 0 },
    currency: { type: "string", default: "USD" },
    showSymbol: { type: "boolean", default: true },
  },
  // ...
});

برای تست این Schema، می‌توان از getBlockType استفاده کرد:

// src/__tests__/block-registration.test.js
import { getBlockType } from "@wordpress/blocks";
import "../index";

describe("price-block registration", () => {
  it("should register with correct attributes", () => {
    const blockType = getBlockType("my-plugin/price-block");
    expect(blockType).toBeDefined();
    expect(blockType.attributes.amount.type).toBe("number");
    expect(blockType.attributes.currency.default).toBe("USD");
  });
});

این تست، ثبت صحیح بلاک و Schema آن را تضمین می‌کند. اگر کسی به‌اشتباه نوع amount را از number به string تغییر دهد، تست شکست می‌خورد.

Snapshot Testing و Attribute Validation

Snapshot Testing یکی از ویژگی‌های قدرتمند Jest است که خروجی یک تابع را با یک نسخه ذخیره‌شده مقایسه می‌کند. در بلاک‌های وردپرس، این تکنیک برای تست save.js ایده‌آل است.

// src/__tests__/save.test.js
import { render } from "@testing-library/react";
import Save from "../save";

describe("Save component", () => {
  it("should render price correctly", () => {
    const attributes = { amount: 99.99, currency: "USD" };
    const { container } = render();
    expect(container.firstChild).toMatchSnapshot();
  });

  it("should handle empty price", () => {
    const attributes = { amount: 0, currency: "USD" };
    const { container } = render();
    expect(container.firstChild).toMatchSnapshot();
  });
});

بار اول که این تست اجرا می‌شود، Jest یک فایل .snap در پوشه __snapshots__ ایجاد می‌کند. بارهای بعدی، خروجی فعلی با این فایل مقایسه می‌شود. اگر خروجی تغییر کند، تست شکست می‌خورد و توسعه‌دهنده باید تصمیم بگیرد که آیا تغییر عمدی است (و Snapshot را به‌روزرسانی کند) یا یک Regression است (و کد را اصلاح کند).

«Snapshot Testing یک شمشیر دو لبه است: از یک سو، پس‌رفت‌های ناخواسته در رندر را فوراً کشف می‌کند. از سوی دیگر، اگر بدون بازبینی به‌روزرسانی شود، به یک تست بی‌ارزش تبدیل می‌شود.»

برای بلاک‌های با Attribute پیچیده، توصیه می‌شود که Snapshot را با Assertionهای صریح ترکیب کنید. Snapshot به‌تنهایی نمی‌تواند تضمین کند که یک Attribute خاص در DOM وجود دارد؛ این کار نیازمند تست‌های هدفمند است:

import { render } from "@testing-library/react";
import Save from "../save";

it("should include currency class on wrapper", () => {
  const attributes = { amount: 100, currency: "EUR" };
  const { container } = render();
  expect(container.querySelector(".currency-eur")).toBeInTheDocument();
});

این ترکیب، هم ساختار کلی را تضمین می‌کند و هم Attributeهای حیاتی را. اگر با ساخت بلاک سفارشی گوتنبرگ از صفر آشنا شده باشید، این الگو را به‌عنوان بخشی از فرآیند توسعه حرفه‌ای می‌شناسید.

تست Deprecated Blockها

بلاک‌های گوتنبرگ می‌توانند چندین deprecated داشته باشند که رفتار نسخه‌های قبلی را شبیه‌سازی می‌کنند. تست این Deprecationها حیاتی است چون اگر نادرست باشند، محتوای قدیمی سایت از کار می‌افتد:

// src/__tests__/deprecated.test.js
import { getBlockType } from "@wordpress/blocks";
import "../index";

describe("deprecated versions", () => {
  it("should have a deprecated version for old schema", () => {
    const blockType = getBlockType("my-plugin/price-block");
    expect(blockType.deprecated).toBeDefined();
    expect(blockType.deprecated.length).toBeGreaterThan(0);
  });

  it("old attribute should migrate to new", () => {
    const blockType = getBlockType("my-plugin/price-block");
    const oldDeprecated = blockType.deprecated[0];
    expect(oldDeprecated.attributes.oldPrice).toBeDefined();
    expect(oldDeprecated.migrate).toBeInstanceOf(Function);
  });
});

این تست تضمین می‌کند که Deprecation به‌درستی تعریف شده و تابع migrate وجود دارد. اگر با گوتنبرگ و آینده ویرایش محتوا در وردپرس آشنا باشید، می‌دانید که Deprecation یکی از پیچیده‌ترین بخش‌های نگهداری بلاک است.

تست E2E با Playwright: تعامل واقعی کاربر

Playwright یک فریم‌ورک اتوماسیون مرورگر است که توسط Microsoft توسعه یافته و از Chromium، Firefox و WebKit پشتیبانی می‌کند. در بلاک‌های وردپرس، Playwright برای تست Workflowهایی استفاده می‌شود که Jest نمی‌تواند پوشش دهد: درج بلاک در ویرایشگر، تغییر تنظیمات، ذخیره نوشته، و بررسی رندر فرانت‌اند.

راه‌اندازی Playwright در پروژه بلاک

اگر از @wordpress/scripts استفاده می‌کنید، Playwright به‌طور خودکار پیکربندی می‌شود. در غیر این صورت، باید به‌صورت دستی نصب و پیکربندی شود:

npm install --save-dev @playwright/test
npx playwright install

فایل playwright.config.js در ریشه پروژه:

import { defineConfig } from "@playwright/test";

export default defineConfig({
  testDir: "./tests/e2e",
  timeout: 60000,
  use: {
    baseURL: "http://localhost:8889",
    trace: "on-first-retry",
    screenshot: "only-on-failure",
  },
  projects: [
    { name: "chromium", use: { browserName: "chromium" } },
    { name: "firefox", use: { browserName: "firefox" } },
  ],
});

پارامتر baseURL به محیط Test اشاره می‌کند که توسط wp-env راه‌اندازی شده است. پارامترهای trace و screenshot برای دیباگ تست‌های شکست‌خورده حیاتی هستند.

نوشتن اولین تست E2E

// tests/e2e/block.spec.js
import { test, expect } from "@playwright/test";

test.describe("Price Block", () => {
  test.beforeEach(async ({ page }) => {
    // Login
    await page.goto("/wp-login.php");
    await page.fill("#user_login", "admin");
    await page.fill("#user_pass", "password");
    await page.click("#wp-submit");
    await page.waitForURL(/wp-admin/);
  });

  test("should insert block and save post", async ({ page }) => {
    await page.goto("/wp-admin/post-new.php");
    
    // Open block inserter
    await page.click('button[aria-label="Add block"]');
    await page.fill('input[placeholder="Search"]', "Price Block");
    
    // Insert block
    await page.click('button:has-text("Price Block")');
    
    // Fill price
    const amountInput = page.locator('input[type="number"]').first();
    await amountInput.fill("99.99");
    
    // Save post
    await page.click('button:has-text("Publish")');
    await page.waitForSelector('.editor-post-publish-panel__header');
    await page.click('button:has-text("Publish")');
    
    // Verify saved
    await expect(page.locator('.components-notice')).toContainText("Post published");
  });
});

این تست، یک Workflow کامل را شبیه‌سازی می‌کند: ورود به پنل، ایجاد نوشته جدید، درج بلاک، پر کردن قیمت، و انتشار. اگر هر مرحله شکست بخورد، تست با جزئیات کامل (Trace، Screenshot، Console Log) گزارش می‌دهد.

تست رندر فرانت‌اند

بعد از ذخیره نوشته، باید رندر فرانت‌اند نیز بررسی شود:

test("should render price on frontend", async ({ page }) => {
  // Assuming post ID 1 exists with price block
  await page.goto("/?p=1");
  
  const priceElement = page.locator(".wp-block-my-plugin-price");
  await expect(priceElement).toBeVisible();
  await expect(priceElement).toContainText("$99.99");
});

این تست تضمین می‌کند که بلاک در فرانت‌اند با فرمت صحیح رندر می‌شود. اگر با ویرایشگر گوتنبرگ و تحول ویرایش محتوا آشنا باشید، این جداسازی بین Editor و Front-end را به‌عنوان یکی از چالش‌های اصلی بلاک‌نویسی می‌شناسید.

ابزارهای Playwright برای وردپرس

تیم Gutenberg یک پکیج اختصاصی به‌نام @wordpress/e2e-test-utils-playwright منتشر کرده که توابع Helper برای تست وردپرس فراهم می‌کند:

import { test, expect } from "@wordpress/e2e-test-utils-playwright";

test("should create post with block", async ({ page, admin, editor }) => {
  await admin.createNewPost();
  await editor.insertBlock({ name: "my-plugin/price-block" });
  await editor.canvas.getByLabel("Amount").fill("49.99");
  
  const postId = await editor.publishPost();
  expect(postId).toBeGreaterThan(0);
});

این Helperها، جزئیات پیچیده تعامل با ویرایشگر گوتنبرگ را پنهان می‌کنند و تست را پایدارتر می‌سازند. توابع مهم عبارتند از:

  • admin.createNewPost(): ایجاد نوشته جدید و انتقال به ویرایشگر
  • admin.visitAdminPage(path): بازدید از یک صفحه مدیریتی
  • editor.insertBlock({ name }): درج بلاک با نام مشخص
  • editor.publishPost(): انتشار نوشته و بازگشت شناسه
  • editor.canvas: دسترسی به iframe ویرایشگر
  • pageUtils.waitForNetworkIdle(): انتظار برای اتمام درخواست‌های شبکه

استفاده از این Helperها، تست‌ها را کوتاه‌تر، پایدارتر و قابل‌خوان‌تر می‌کند. اگر با مقایسه ابزارهای توسعه وردپرس آشنا شده باشید، این پکیج را به‌عنوان استاندارد رسمی تست بلاک‌های گوتنبرگ می‌شناسید.

یکپارچه‌سازی با CI/CD و GitHub Actions

تست‌ها تنها زمانی ارزش کامل خود را نشان می‌دهند که به‌صورت خودکار در CI/CD اجرا شوند. GitHub Actions یک بستر عالی برای این کار فراهم می‌کند:

# .github/workflows/test.yml
name: Test
on: [push, pull_request]

jobs:
  unit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npm run test:unit

  e2e:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npx wp-env start
      - run: npx playwright install --with-deps
      - run: npm run test:e2e
      - uses: actions/upload-artifact@v4
        if: failure()
        with:
          name: playwright-traces
          path: test-results/

این Workflow دو Job مجزا دارد: یکی برای تست‌های واحد که سریع اجرا می‌شوند، و یکی برای تست‌های E2E که کندتر هستند. اگر تست‌های E2E شکست بخورند، Trace و Screenshot به‌عنوان Artifact ذخیره می‌شود تا دیباگ آسان‌تر باشد.

نکته مهم در CI/CD، اجرای تست‌ها به‌صورت موازی است. Jest به‌صورت پیش‌فرض تست‌ها را موازی اجرا می‌کند. Playwright نیز با تنظیم workers می‌تواند چند مرورگر را همزمان اجرا کند:

export default defineConfig({
  workers: process.env.CI ? 2 : undefined,
  retries: process.env.CI ? 2 : 0,
});

پارامتر retries باعث می‌شود تست‌های شکست‌خورده در CI دو بار دیگر تلاش شوند. این کار، شکنندگی تست‌های E2E را کاهش می‌دهد. اگر با ساختاربندی پروژه توسعه وردپرس آشنا باشید، این سطح از اتوماسیون را به‌عنوان بلوغ تیم مهندسی می‌شناسید.

اشتباهات رایج در تست بلاک‌های وردپرس

اشتباه اول: نوشتن تست E2E برای همه‌چیز. Playwright کند و شکننده است. هر تستی که می‌تواند در Jest نوشته شود، باید در Jest نوشته شود. نسبت ایده‌آل، ۷۰٪ Jest و ۳۰٪ Playwright است.

اشتباه دوم: استفاده از Selectorهای شکننده در Playwright. انتخاب بر اساس Class CSS یا ساختار DOM، با هر تغییر در گوتنبرگ می‌شکند. به‌جای آن، از Selectorهای معنایی استفاده کنید:

// ❌ شکننده
await page.click('.components-button.is-primary');

// ✅ پایدار
await page.getByRole('button', { name: 'Publish' }).click();

اشتباه سوم: نادیده گرفتن waitFor. گوتنبرگ یک اپلیکیشن React است و تعاملات آن آسنکرون هستند. اگر بدون انتظار کلیک کنید، تست ممکن است قبل از آماده شدن UI اجرا شود و شکست بخورد. از waitForSelector، waitForLoadState، یا Helperهای @wordpress/e2e-test-utils-playwright استفاده کنید.

اشتباه چهارم: به‌روزرسانی کور Snapshotها. اگر یک Snapshot شکست خورد، هرگز بدون بازبینی با --updateSnapshot آن را به‌روز نکنید. Snapshot یک قرارداد است؛ اگر تغییر عمدی است، در Commit Message توضیح دهید. اگر تغییر ناخواسته است، کد را اصلاح کنید. اگر با اشتباهات رایج در توسعه قالب و افزونه وردپرس آشنا شده باشید، این اصل را در همه لایه‌های توسعه می‌شناسید.

اشتباه پنجم: تست نکردن Deprecationها. اگر بلاک شما چند نسخه دارد، هر نسخه باید تست جداگانه داشته باشد. بدون این تست‌ها، محتوای قدیمی سایت ممکن است در به‌روزرسانی بعدی از کار بیفتد.

اشتباه ششم: نادیده گرفتن تست Accessibility. بلاک‌های گوتنبرگ باید استانداردهای ARIA را رعایت کنند. Playwright می‌تواند با @axe-core/playwright تست‌های Accessibility را نیز اجرا کند:

import { test, expect } from "@playwright/test";
import AxeBuilder from "@axe-core/playwright";

test("block should have no accessibility violations", async ({ page }) => {
  await page.goto("/?p=1");
  const results = await new AxeBuilder({ page }).analyze();
  expect(results.violations).toEqual([]);
});

اگر با استانداردهای دسترس‌پذیری وب آشنا باشید، این نوع تست را به‌عنوان بخشی از مسئولیت مهندسی می‌شناسید، نه یک ویژگی اضافی.

پرسش‌های پرتکرار درباره تست بلاک‌های وردپرس

آیا تست بلاک‌های وردپرس برای پروژه‌های کوچک هم ضروری است؟

بله، اما با شدت کمتر. برای یک پروژه کوچک با یک یا دو بلاک سفارشی، حداقل تست‌های Jest برای منطق و Attributeها کافی است. تست‌های E2E را می‌توان به Critical Pathها محدود کرد. هرچه پروژه بزرگ‌تر و تیم بزرگ‌تر، ارزش تست بیشتر می‌شود.

تفاوت Jest و Playwright در تست بلاک‌های وردپرس چیست؟

Jest در محیط Node.js بدون مرورگر اجرا می‌شود و برای تست منطق، توابع Helper، اعتبارسنجی Attributeها و Snapshotهای رندر مناسب است. Playwright یک مرورگر واقعی را کنترل می‌کند و برای تست Workflowهای کاربر، درج بلاک در ویرایشگر، ذخیره نوشته و رندر فرانت‌اند استفاده می‌شود. این دو مکمل یکدیگرند، نه جانشین.

چگونه تست‌های Playwright را پایدارتر کنیم؟

سه اصل: اول، از Selectorهای معنایی (Role، Label، Text) به‌جای Class CSS استفاده کنید. دوم، از Helperهای @wordpress/e2e-test-utils-playwright بهره ببرید. سوم، در CI از retries و در پیکربندی از trace و screenshot برای دیباگ استفاده کنید.

آیا تست بلاک‌ها با wp-env کند است؟

wp-env از Docker استفاده می‌کند و راه‌اندازی اولیه آن ممکن است چند دقیقه طول بکشد. اما پس از راه‌اندازی، اجرای تست‌ها سریع است. برای CI، می‌توان از کش Docker و اجرای موازی استفاده کرد تا زمان کل کاهش یابد.

چگونه تست‌ها را در CI/CD اجرا کنیم؟

در GitHub Actions، دو Job جداگانه تعریف کنید: یکی برای npm run test:unit (سریع) و یکی برای npm run test:e2e (کند). برای E2E، باید wp-env و Playwright نصب شوند. Trace و Screenshot را در صورت شکست به‌عنوان Artifact ذخیره کنید.

آیا Jest جایگزین تست‌های PHPUnit می‌شود؟

خیر. Jest برای تست JavaScript بلاک‌هاست و PHPUnit برای تست کد PHP وردپرس. اگر بلاک شما یک Endpoint REST یا یک تابع PHP دارد، باید آن را با PHPUnit تست کنید. این دو ابزار، دو لایه متفاوت از یک هرم تست را پوشش می‌دهند. اگر با دیباگ کردن کدهای سفارشی وردپرس آشنا شده باشید، این جداسازی را به‌عنوان یک اصل می‌شناسید.

آیا تست بلاک‌ها برای همه نسخه‌های وردپرس ضروری است؟

بلاک‌های گوتنبرگ از وردپرس ۵.۰ وجود دارند، اما API آن‌ها در نسخه‌های مختلف تغییر کرده. توصیه می‌شود تست‌ها در حداقل دو نسخه وردپرس اجرا شوند: نسخه فعلی و نسخه قبلی. اگر با بررسی سازگاری افزونه‌های وردپرس آشنا باشید، این موضوع را به‌عنوان بخشی از استراتژی سازگاری می‌شناسید.

نگاه مهندسی سطح بالا

از منظر معماری نرم‌افزار، تست بلاک‌های گوتنبرگ یک نمونه جالب از Layered Testing در یک محیط Runtime چندگانه است. بلاک‌ها در سه محیط (Editor، Front-end، Serializer) اجرا می‌شوند و هر محیط چرخه حیات، وابستگی‌ها و APIهای متفاوتی دارد. Jest لایه‌های Editor و Serializer را در محیط Node.js شبیه‌سازی می‌کند، در حالی که Playwright لایه Front-end را در مرورگر واقعی. این جداسازی، نه فقط یک انتخاب فنی، بلکه یک تصمیم معمارانه است: تست‌هایی که می‌توانند مستقل از مرورگر اجرا شوند، باید در Jest نوشته شوند تا هزینه CI پایین بماند و بازخورد سریع‌تر باشد.

چالش اصلی در تست بلاک‌های گوتنبرگ، State Synchronization است. گوتنبرگ یک اپلیکیشن React با State Management پیچیده (بر پایه @wordpress/data) است که در آن، State بین ویرایشگر، Store و Server همگام‌سازی می‌شود. تست این همگام‌سازی در Jest نیازمند Mock کردن @wordpress/data است که خود یک لایه پیچیدگی دارد. Playwright این مشکل را با اجرای مرورگر واقعی حل می‌کند، اما هزینه آن کندی و شکنندگی است. تعادل بهینه، استفاده از Mock Store در Jest برای تست‌های واحد و استفاده از Playwright فقط برای Workflowهای Critical است. اگر با استفاده از استانداردهای کدنویسی وردپرس در پروژه‌ها آشنا باشید، این سطح از انضباط مهندسی را به‌عنوان بلوغ تیم می‌شناسید. در نهایت، تست بلاک‌های گوتنبرگ یک سرمایه‌گذاری است: زمان اولیه بیشتر، اما هزینه نگهداری بلندمدت به‌شدت کمتر. تیم‌هایی که این سرمایه‌گذاری را انجام می‌دهند، در برابر تغییرات سریع اکوسیستم گوتنبرگ مقاوم‌تر هستند و تجربه کاربری پایدارتری به کاربران نهایی ارائه می‌دهند.

اگر این تجربه را در یک پروژه واقعی داشته‌اید، جالب است بدانید کدام بخش آن بیشترین زمان را از شما گرفت. تجربه خودتان را در دیدگاه‌ها بنویسید؛ به‌خصوص اگر راه‌حل دیگری پیدا کرده‌اید که می‌تواند برای خواننده بعدی مفید باشد. 🧪

همچنین اگر می‌خواهید در مورد پیاده‌سازی عملی بلاک‌های گوتنبرگ و تست آن‌ها بیشتر بدانید، ساخت بلاک سفارشی گوتنبرگ از صفر و مقایسه ویرایشگر Classic و گوتنبرگ می‌توانند نقاط شروع خوبی باشند.