•19 min read

Kiểm thử hồi quy trực quan và hướng dẫn so khớp pixel với Playwright

Kiểm thử hồi quy trực quan và hướng dẫn so khớp pixel với Playwright

Tất cả chúng ta đều đã từng trải qua: bạn hợp nhất một bản sửa lỗi CSS đơn giản cho tiêu đề, và đột nhiên phần chân trang trên trang thanh toán bị hỏng hoàn toàn. Lỗi hình ảnh là những hồi quy khó phát hiện nhất bằng các bài kiểm tra đơn vị tiêu chuẩn, đó là lý do tại sao kiểm thử hồi quy hình ảnh lại rất quan trọng.

Tin tốt là gì? Bạn không cần một nền tảng SaaS bên thứ ba đắt tiền để thực hiện điều đó nữa. Playwright có các công cụ so sánh hình ảnh tích hợp được cung cấp bởi một triển khai C++ cực nhanh của thư viện Pixelmatch. Nó cho phép bạn phát hiện các thay đổi bố cục xuống từng pixel mà không cần rời khỏi trình chạy thử nghiệm tiêu chuẩn của bạn.

Đây chính xác là cách tôi thiết lập kiểm thử hồi quy hình ảnh Playwright để nhanh chóng, đáng tin cậy và hoàn toàn không có lỗi trên các hệ điều hành khác nhau.

Audio Briefing
0:00 / 0:00
Part of a Series

Bộ kiểm thử & QA Frontend hiện đại

Part 4 of 4

Cách Pixelmatch của Playwright thực sự hoạt động

Khi bạn chạy một bài kiểm tra hình ảnh, Playwright khởi động một trình duyệt không giao diện, chụp một bộ đệm ảnh chụp màn hình PNG và so sánh nó từng byte với một ảnh chụp nhanh "vàng" mà bạn đã lưu trước đó.

Nhưng nó thông minh hơn một phép so sánh đơn giản. Thuật toán Pixelmatch chuyển đổi màu RGB sang không gian màu YIQ (mô phỏng nhận thức thị giác của con người). Điều này có nghĩa là nó có thể bỏ qua các tạo tác khử răng cưa phông chữ nhỏ trong khi vẫn phát hiện các lỗi căn chỉnh bố cục thực sự.

Kiến trúc hồi quy hình ảnh Pixelmatch

Khi đánh giá sự khác biệt về hình ảnh, Pixelmatch chuyển đổi các giá trị màu RGB thành các biểu diễn độ chói và sắc độ YIQ. Chuyển đổi không gian màu này đảm bảo rằng các biến thể khử răng cưa phông chữ tinh tế không được ghi nhận là lỗi hình ảnh dương tính giả trong khi các lỗi căn chỉnh bố cục thực sự kích hoạt lỗi kiểm tra ngay lập tức.

// Example: Basic visual regression test assertion in Playwright
import { test, expect } from '@playwright/test';

test('verify landing page visual layout matches baseline snapshot', async ({ page }) => {
  await page.goto('https://app.example.com');
  await page.waitForLoadState('networkidle');
  
  // Mask changing elements like animated carousels and timestamps
  await expect(page).toHaveScreenshot('landing-page-baseline.png', {
    mask: [page.locator('.hero-carousel-timer'), page.locator('.user-avatar')],
    fullPage: true,
    maxDiffPixelRatio: 0.01, // Allow 1% pixel variance across full viewport
  });
});

Để hiểu cách Playwright xử lý việc so sánh ảnh chụp màn hình, cần phải xem xét cách các hình ảnh cơ sở được tạo và lưu trữ trong kiểm soát nguồn. Khi thực thi expect(page).toHaveScreenshot() lần đầu tiên, Playwright sẽ báo lỗi thiếu cơ sở và ghi một tệp PNG ảnh chụp nhanh vàng vào thư mục __snapshots__ liền kề với tệp kiểm tra của bạn. Trong các lần chạy tiếp theo, Playwright sẽ chụp một ảnh chụp màn hình mới và so sánh nó với cơ sở.

// Advanced snapshot comparison configuration in playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  expect: {
    toHaveScreenshot: {
      threshold: 0.2, // Individual pixel color delta threshold (0.0 to 1.0)
      maxDiffPixels: 50, // Absolute pixel count diff limit
      maxDiffPixelRatio: 0.005, // Ratio diff limit (0.5% of total image area)
      animations: 'disabled', // Automatically pause CSS animations and transitions
      caret: 'hide', // Hide blinking text input carets
      scale: 'css', // Render screenshots at CSS pixel resolution rather than physical device DPI
    },
  },
  use: {
    viewport: { width: 1280, height: 720 },
    deviceScaleFactor: 1, // Enforce 1x scale factor to prevent Retina display pixel mismatches
  },
});

Theo mặc định, Playwright tắt hoạt ảnh CSS và ẩn các dấu nháy nhập nhấp nháy trong quá trình chụp ảnh màn hình. Các điều khiển ổn định tích hợp này loại bỏ các nguồn phổ biến gây ra sự không ổn định của kiểm thử hình ảnh trước khi các thuật toán so sánh hình ảnh được gọi.

Ngoài việc khớp hình ảnh cơ bản, so sánh hình ảnh trong Playwright hoạt động ở tốc độ thực thi C++ gốc. Thay vì truyền bộ đệm hình ảnh qua các ranh giới cầu nối JavaScript, Playwright ủy quyền việc so sánh hình ảnh nhị phân trực tiếp cho các quy trình C++ đã biên dịch. Tối ưu hóa hiệu suất gốc này cho phép Playwright so sánh các ảnh chụp màn hình toàn trang 4K trong vòng chưa đầy hai mươi mili giây.

// Custom reporter for visual snapshot diff diagnostics
import { Reporter, TestCase, TestResult } from '@playwright/test/reporter';

class VisualDiffReporter implements Reporter {
  onTestEnd(test: TestCase, result: TestResult) {
    if (result.status === 'failed') {
      const visualErrors = result.errors.filter(e => e.message?.includes('Screenshot comparison failed'));
      if (visualErrors.length > 0) {
        console.log(`Visual regression failure detected in test: ${test.title}`);
        console.log(`Inspect visual diff artifacts in test-results folder.`);
      }
    }
  }
}

export default VisualDiffReporter;
Advertisement

Cách hiệu chỉnh MaxDiffPixels và Tỷ lệ ngưỡng?

Hiệu chỉnh ngưỡng so sánh hình ảnh đòi hỏi phải cân bằng độ nhạy của kiểm thử với khả năng chịu đựng các biến thể hiển thị lành tính. Đặt ngưỡng quá chặt sẽ gây ra lỗi xây dựng sai trên các bản cập nhật hiển thị phụ pixel nhỏ của trình duyệt, trong khi đặt chúng quá lỏng lẻo sẽ cho phép các bố cục UI bị hỏng thoát khỏi sự phát hiện.

Sơ đồ hiệu chỉnh ngưỡng

Playwright cung cấp ba tham số điều chỉnh độ nhạy riêng biệt bên trong toHaveScreenshot: threshold, maxDiffPixels và maxDiffPixelRatio. Mỗi tham số kiểm soát một khía cạnh khác nhau của việc phát hiện sự khác biệt hình ảnh.

// Example: Component-level visual regression with explicit threshold tuning
import { test, expect } from '@playwright/test';

test('verify checkout button hover state visual fidelity', async ({ page }) => {
  await page.goto('https://app.example.com/checkout');
  const payButton = page.locator('button#pay-now');
  
  await payButton.hover();
  await expect(payButton).toHaveScreenshot('pay-button-hover.png', {
    threshold: 0.1, // Strict color matching for brand button background gradients
    maxDiffPixels: 10, // Limit allowed pixel delta to minor border anti-aliasing
  });
});

Tham số threshold định nghĩa ngưỡng khoảng cách màu sắc cảm nhận từ 0.0 (nghiêm ngặt nhất) đến 1.0 (khoan dung nhất). Giá trị 0.2 đại diện cho sự cân bằng mặc định, bỏ qua các biến thể khử răng cưa phụ pixel nhỏ do raster hóa phông chữ GPU gây ra trong khi phát hiện các thay đổi mã hex màu tinh tế.

Tham số cấu hìnhGiá trị mặc địnhPhạm vi khuyến nghịMục đích
threshold0.20.1 - 0.3Dung sai delta màu trên mỗi kênh pixel
maxDiffPixelsundefined20 - 200Giới hạn cứng trên tổng số pixel khác nhau được phép
maxDiffPixelRatioundefined0.001 - 0.01Phần trăm tổng diện tích pixel ảnh chụp màn hình được phép khác nhau

Tham số maxDiffPixels đặt ngưỡng số lượng tuyệt đối cho các pixel khác nhau trên toàn bộ hình ảnh. Điều này hữu ích cho các kiểm thử cấp thành phần, nơi bạn muốn cho phép tối đa 15 pixel biến thể hiển thị phông chữ nhưng từ chối bất kỳ sự dịch chuyển phần tử lớn hơn nào. Đối với ảnh chụp màn hình toàn trang, maxDiffPixelRatio được ưu tiên vì nó tỷ lệ thuận với chiều cao trang và độ phân giải khung nhìn.

// Custom Playwright test fixture for custom threshold override per viewport
import { test as baseTest, expect } from '@playwright/test';

export const test = baseTest.extend({
  assertVisualMatch: [async ({ page }, use) => {
    const customMatch = async (name: string, maxRatio = 0.005) => {
      await expect(page).toHaveScreenshot(name, {
        maxDiffPixelRatio: maxRatio,
        stylePath: './e2e/styles/visual-test-overrides.css', // Inject custom CSS to hide changing elements
      });
    };
    await use(customMatch);
  }, { auto: false }],
});

Sử dụng các fixture kiểm thử tùy chỉnh cho phép các nhóm kỹ thuật tập trung các quy tắc khẳng định hình ảnh trong khi cho phép các tác giả đặc tả ghi đè các ngưỡng cho các trang phức tạp chứa biểu đồ canvas hoặc đồ họa WebGL.

Khi làm việc với các thiết kế web đáp ứng, việc kiểm thử nhiều kích thước khung nhìn đảm bảo tính toàn vẹn bố cục trên màn hình di động, máy tính bảng và máy tính để bàn. Cấu hình mảng ma trận khung nhìn trong playwright.config.ts tự động chạy các khẳng định hình ảnh đối với các cấu hình thiết bị mục tiêu.

// Responsive viewport visual testing matrix configuration
export const responsiveProjects = [
  {
    name: 'mobile-chrome',
    use: { viewport: { width: 375, height: 667 }, deviceScaleFactor: 2 },
  },
  {
    name: 'tablet-chrome',
    use: { viewport: { width: 768, height: 1024 }, deviceScaleFactor: 1 },
  },
  {
    name: 'desktop-chrome',
    use: { viewport: { width: 1440, height: 900 }, deviceScaleFactor: 1 },
  },
];

Bạn xử lý sự không ổn định của phông chữ và hiển thị đa nền tảng như thế nào?

Sự khác biệt về hiển thị phông chữ đa nền tảng là nguyên nhân phổ biến nhất gây ra sự không ổn định của kiểm thử hình ảnh khi chạy kiểm thử cục bộ trên macOS hoặc Windows và xác minh chúng trên các trình chạy CI Linux. Các hệ điều hành hiển thị phông chữ khác nhau trên các nền tảng, dẫn đến các chữ ký khử răng cưa pixel khác nhau cho cùng một nội dung văn bản.

Tạo tác hiển thị đa nền tảng

Thực hiện các kiểm thử hồi quy hình ảnh trên các nền tảng hệ điều hành khác nhau chắc chắn dẫn đến sự không khớp ảnh chụp nhanh. Thực hành tốt nhất trong ngành để đạt được thực thi kiểm thử hình ảnh xác định 100 phần trăm là đóng gói trình chạy Playwright bằng cách sử dụng các hình ảnh Docker Playwright chính thức.

# Running Playwright visual regression tests inside Docker locally to match CI
docker run --rm -it \
  -v $(pwd):/work \
  -w /work \
  mcr.microsoft.com/playwright:v1.44.0-jammy \
  npx playwright test --update-snapshots

Bằng cách thực hiện các bản cập nhật ảnh chụp nhanh và chạy kiểm thử bên trong cùng một hình ảnh Docker Ubuntu Linux (mcr.microsoft.com/playwright), hiển thị phông chữ, bản dựng nhị phân công cụ trình duyệt và raster hóa canvas GPU tạo ra đầu ra pixel giống hệt nhau bất kể hệ điều hành nhà phát triển máy chủ.

// Injecting CSS rules to freeze web fonts and hide changing elements during visual tests
import { test, expect } from '@playwright/test';

test.beforeEach(async ({ page }) => {
  // Inject global CSS overrides before taking visual snapshots
  await page.addInitScript(() => {
    const style = document.createElement('style');
    style.innerHTML = `
      *, *::before, *::after {
        animation-duration: 0s !important;
        animation-delay: 0s !important;
        transition-duration: 0s !important;
        transition-delay: 0s !important;
      }
      /* Hide live chat bubbles and date elements */
      .live-chat-bubble, .realtime-date-display {
        visibility: hidden !important;
      }
    `;
    document.head.appendChild(style);
  });
});

Ngoài việc đóng gói Docker, các chiến lược tải phông chữ web phải được quản lý trong quá trình chạy kiểm thử. Các phông chữ web được tải không đồng bộ từ các CDN bên ngoài thường gây ra các thay đổi bố cục nếu ảnh chụp màn hình được chụp trước khi phông chữ tải hoàn chỉnh. Gọi document.fonts.ready trước khi khẳng định hình ảnh đảm bảo rằng các phông chữ hệ thống dự phòng không tạo ra các khác biệt hình ảnh sai.

// Helper utility to wait for all web fonts to load prior to screenshot capture
export async function waitForFontsLoaded(page: import('@playwright/test').Page) {
  await page.evaluate(async () => {
    await document.fonts.ready;
  });
}

Xử lý màn hình Retina mật độ cao đòi hỏi sự chú ý đặc biệt khi thiết lập các yếu tố tỷ lệ thiết bị. Màn hình máy tính để bàn thường hoạt động ở deviceScaleFactor: 1, trong khi màn hình di động và MacBook hiển thị ở deviceScaleFactor: 2. Việc buộc rõ ràng deviceScaleFactor: 1 trong các tùy chọn ngữ cảnh Playwright chuẩn hóa kích thước pixel ảnh chụp màn hình trên các phần cứng phát triển đa dạng.

// Standardizing device scale factor across developer machines
export const visualContextOptions = {
  deviceScaleFactor: 1,
  hasTouch: false,
  isMobile: false,
  javaScriptEnabled: true,
};

Chiến lược lưu trữ và che giấu ảnh chụp nhanh CI lý tưởng là gì?

Quản lý các tệp ảnh chụp nhanh cơ sở trong kho Git đòi hỏi tổ chức cẩn thận để ngăn chặn sự phình to của kho trong khi cho phép xem xét mã khác biệt hình ảnh rõ ràng trên các yêu cầu kéo. Vì các tệp PNG nhị phân không thể được hợp nhất bằng các công cụ khác biệt văn bản Git tiêu chuẩn, nên các tài sản ảnh chụp nhanh hình ảnh phải được đặt tên một cách xác định theo nền tảng và trình duyệt.

Kiến trúc lưu trữ ảnh chụp nhanh CI

Playwright định dạng tên tệp ảnh chụp nhanh bằng cách thêm nền tảng hệ điều hành, tên công cụ trình duyệt và kích thước khung nhìn vào định danh ảnh chụp nhanh (ví dụ: landing-page-chromium-linux.png). Hậu tố nền tảng này ngăn các ảnh chụp nhanh cơ sở macOS ghi đè các ảnh chụp nhanh CI Linux khi được commit vào Git.

# GitHub Actions workflow for automated visual regression test reporting
name: Visual Regression Tests
on: [pull_request]

jobs:
  visual-test:
    runs-on: ubuntu-latest
    container:
      image: mcr.microsoft.com/playwright:v1.44.0-jammy
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'
      - name: Install Dependencies
        run: npm ci
      - name: Run Playwright Visual Tests
        run: npx playwright test --project=chromium
      - name: Upload Visual Diff Artifacts on Failure
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: playwright-visual-diffs
          path: test-results/
          retention-days: 7

Khi một khẳng định hình ảnh thất bại trong CI, Playwright tạo một báo cáo khác biệt ba hình ảnh chứa hình ảnh cơ sở gốc, ảnh chụp màn hình thực tế được chụp và một hình ảnh khác biệt làm nổi bật các pixel đã thay đổi bằng màu đỏ tươi. Tải thư mục test-results/ lên dưới dạng một artifact xây dựng CI cho phép các nhà phát triển kiểm tra các lỗi hình ảnh trực tiếp trong trình duyệt của họ.

// Masking complex changing UI elements like maps and embedded video frames
import { test, expect } from '@playwright/test';

test('verify dashboard layout with masked changing elements', async ({ page }) => {
  await page.goto('https://app.example.com/analytics');
  
  // Locate changing elements that alter content on every page load
  const liveStockTicker = page.locator('.stock-ticker-stream');
  const userNotificationBadge = page.locator('.notification-count');
  
  await expect(page).toHaveScreenshot('analytics-dashboard.png', {
    mask: [liveStockTicker, userNotificationBadge],
    maskColor: '#FF00FF', // Custom magenta color for masked regions in diff outputs
  });
});

Sử dụng tùy chọn mask phủ các hình chữ nhật màu đặc lên các phần tử DOM đang thay đổi trước khi chụp ảnh màn hình. Kỹ thuật này cho phép các nhóm kỹ thuật xác minh bố cục toàn trang trong khi bỏ qua các biểu ngữ quảng cáo của bên thứ ba không thể đoán trước, luồng video hoặc ảnh hồ sơ người dùng.

// Masking strategy for HTML canvas elements and data charts
export async function maskCanvasElements(page: import('@playwright/test').Page) {
  const chartLocators = page.locator('canvas.recharts-surface');
  const count = await chartLocators.count();
  const locatorsArray = [];
  
  for (let i = 0; i < count; i++) {
    locatorsArray.push(chartLocators.nth(i));
  }
  
  return locatorsArray;
}
Advertisement

Các nhóm nên cấu trúc quy trình làm việc ảnh chụp nhanh trực quan như thế nào?

Thiết lập các quy trình làm việc tổ chức rõ ràng để cập nhật ảnh chụp nhanh cơ sở đảm bảo rằng kiểm thử hình ảnh đẩy nhanh việc phân phối phần mềm thay vì gây ra tắc nghẽn phê duyệt PR. Kiểm thử hồi quy hình ảnh nên được tích hợp vào các quy trình xem xét mã yêu cầu kéo tiêu chuẩn.

Khi một nhánh tính năng cập nhật hợp lệ thiết kế thành phần UI, các nhà phát triển nên cập nhật ảnh chụp nhanh cơ sở trong Docker cục bộ, kiểm tra các khác biệt được tạo và commit các tệp PNG đã cập nhật cùng với các thay đổi mã của họ. Người đánh giá yêu cầu kéo sau đó có thể xem xét cả mã thành phần và hình ảnh ảnh chụp nhanh hình ảnh trực tiếp trong giao diện GitHub hoặc GitLab.

// Script: Automating local visual snapshot updates in Docker environment
import { execSync } from 'child_process';

function updateVisualSnapshots() {
  console.log('Updating visual baseline snapshots inside Docker container...');
  
  const dockerCmd = `docker run --rm -v ${process.cwd()}:/work -w /work mcr.microsoft.com/playwright:v1.44.0-jammy npx playwright test --update-snapshots`;
  
  try {
    execSync(dockerCmd, { stdio: 'inherit' });
    console.log('Visual snapshots updated successfully. Inspect Git diff before committing.');
  } catch (error) {
    console.error('Failed to update visual snapshots.');
    process.exit(1);
  }
}

updateVisualSnapshots();

Kiểm thử hồi quy hình ảnh cung cấp một mạng lưới an toàn bảo vệ cho các ứng dụng web hiện đại. Bằng cách thành thạo hiệu chỉnh ngưỡng Pixelmatch, thực thi thực thi kiểm thử dựa trên Docker và áp dụng che giấu phần tử DOM thông minh, các nhóm kỹ thuật có thể phát hiện các hồi quy hình ảnh sớm trong CI trong khi duy trì các bộ kiểm thử không có lỗi.

Tích hợp xác thực ảnh chụp nhanh hình ảnh tự động vào các đường ống triển khai liên tục tạo ra sự tự tin trong các bộ phận thiết kế và kỹ thuật. Các nhóm có thể tái cấu trúc các khung CSS phức tạp, nâng cấp thư viện thành phần và hiện đại hóa các hệ thống thiết kế với sự đảm bảo hoàn toàn rằng các hồi quy hình ảnh không mong muốn sẽ được phát hiện trước khi đến tay người dùng cuối.

Thiết lập các chính sách xem xét khác biệt hình ảnh tự động giúp duy trì các thư mục ảnh chụp nhanh cơ sở sạch sẽ theo thời gian. Xóa các tệp ảnh chụp nhanh cũ cho các tuyến trang không dùng nữa và kiểm tra việc sử dụng lưu trữ Git LFS đảm bảo rằng các bộ kiểm thử hình ảnh vẫn nhanh chóng và dễ bảo trì khi sản phẩm mở rộng.

Thêm kiểm tra hồi quy hình ảnh vào các cổng yêu cầu kéo giảm chu kỳ kiểm thử QA thủ công từ hàng giờ xuống còn vài phút. Các nhóm phát triển có thể triển khai các cải tiến UI nhanh chóng trong khi vẫn duy trì các tiêu chuẩn hình ảnh chính xác trên tất cả các công cụ trình duyệt và kích thước màn hình được hỗ trợ.

Bạn cũng có thể thích

Các câu hỏi thường gặp

Playwright sử dụng thư viện nào để so sánh ảnh chụp màn hình trực quan?

Playwright sử dụng một liên kết C++ nhanh, gốc của thư viện Pixelmatch. Nó so sánh các bộ đệm ảnh chụp màn hình từng pixel bằng cách sử dụng các phép tính delta không gian màu YIQ để xác định sự khác biệt hình ảnh cảm nhận trong khi bỏ qua các biến thể khử răng cưa phụ pixel tinh tế.

Tại sao các kiểm thử hồi quy hình ảnh vượt qua cục bộ trên macOS nhưng thất bại trên Linux CI?

Các hệ điều hành hiển thị phông chữ khác nhau trên các nền tảng. macOS sử dụng khử răng cưa Quartz, trong khi Linux sử dụng FreeType. Những khác biệt về raster hóa phông chữ này gây ra các chữ ký pixel khác nhau. Chạy các kiểm thử hình ảnh bên trong vùng chứa Docker Playwright chính thức đảm bảo hiển thị giống hệt nhau giữa các máy cục bộ và CI.

Làm cách nào để cập nhật ảnh chụp nhanh cơ sở Playwright khi thiết kế UI thay đổi?

Bạn có thể cập nhật hình ảnh ảnh chụp nhanh cơ sở bằng cách chạy npx playwright test --update-snapshots trong terminal của mình. Để có tính nhất quán đa nền tảng, hãy chạy lệnh này bên trong vùng chứa Docker Playwright chính thức để các tệp PNG đã cập nhật khớp với kỳ vọng của Linux CI.

Sự khác biệt giữa threshold và maxDiffPixels trong Playwright là gì?

Tham số threshold định nghĩa dung sai khoảng cách màu sắc cảm nhận cho một pixel riêng lẻ (từ 0.0 đến 1.0). Tham số maxDiffPixels đặt giới hạn số lượng tuyệt đối về tổng số pixel trong toàn bộ hình ảnh được phép khác nhau trước khi kiểm thử thất bại.

Làm cách nào để tôi có thể loại trừ các phần tử thay đổi như ngày tháng khỏi ảnh chụp nhanh hình ảnh?

Bạn có thể loại trừ các phần tử thay đổi bằng cách truyền một mảng các bộ định vị Playwright vào tùy chọn mask trong toHaveScreenshot(). Playwright tự động che các phần tử bộ định vị đó bằng các hình chữ nhật màu đặc trước khi chụp bộ đệm ảnh chụp màn hình.

Tôi có nên commit các tệp PNG ảnh chụp nhanh Playwright vào Git không?

Có, việc commit các ảnh chụp nhanh PNG cơ sở vào Git đảm bảo rằng các đánh giá yêu cầu kéo theo dõi các thay đổi UI hình ảnh cùng với các sửa đổi mã. Để giữ kích thước kho lưu trữ có thể quản lý được, chỉ lưu trữ các ảnh chụp nhanh cấp thành phần hoặc ảnh chụp nhanh toàn trang được nhắm mục tiêu thay vì các khung nhìn dư thừa.

Share this article:

Stay Updated

Get the latest posts delivered straight to your inbox.

Free Developer Utilities

Free In-Browser Developer Tools

Clean AI CLI logs, build cron expressions, decode JWTs, and calculate chmod permissions offline.

Explore Tools
Advertisement