Danh sách bài viết

Bài 54: Multi-Browser Matrix — Chromium / Firefox / WebKit

Multi-browser matrix là cách tổ chức projects config để chạy cùng một test suite trên nhiều engine, nhằm phát hiện cross-browser compatibility bug trước khi ra production. Bài này đi sâu vào chiến lược thiết kế matrix — từ smoke-only Chromium trên PR cho đến full 3-browser chạy nightly, cách xử lý visual snapshot riêng từng engine, conditional skip, branded browsers, CI trade-off và các limitation thực tế của WebKit Linux và Firefox CDP partial.

28/05/2026
0 lượt xem
1

Mục Tiêu Bài Học

Sau bài này bạn sẽ:

  • Biết cách config projects[] cho 3 engine và branded browsers trong playwright.config.ts.
  • Thiết kế được chiến lược matrix phù hợp: smoke-only Chromium trên PR, full 3-browser chạy nightly.
  • Biết cách dùng test.skip()test.describe conditional để bỏ qua feature không support trên browser cụ thể.
  • Xử lý đúng visual snapshot khi mỗi engine render khác nhau.
  • Hiểu trade-off CI time khi nhân 3 projects và cách WebKit Linux khác Safari thật.

Bài này khác với Series 1 bài 43 (giải thích 3 engine là gì, khi nào dùng browser nào). Ở đây tập trung vào cách tổ chức matrix ở quy mô project — chiến lược, pattern, và limitation thực tế khi vận hành CI.

2

Vì Sao Cần Matrix (Không Chỉ Chromium)

Chạy test chỉ trên Chromium phát hiện được phần lớn logic bug — nhưng có một nhóm bug chỉ lộ ra khi test thêm Firefox hoặc WebKit:

Cross-Browser Bug

Feature hoạt động đúng trên Chrome nhưng break trên Safari — thường gặp với CSS property mới, Web API chưa fully supported hoặc event behavior khác.

// Ví dụ: dialog xử lý khác nhau
// Chrome: dialog close khi click overlay
// Firefox: tùy version, event bubbling khác
// WebKit: focus management sau close có thể khác

CSS Rendering Difference

Layout tính toán khác giữa Blink (Chromium) và Gecko (Firefox). Ví dụ: subgrid, container queries, gap trong flexbox — mỗi engine có thời điểm ship khác nhau và edge case riêng.

JavaScript Engine Quirk

V8 (Chromium), SpiderMonkey (Firefox), JavaScriptCore (WebKit) xử lý một số edge case spec khác nhau — đặc biệt với number precision, regex behavior, và Promise microtask timing trong test async phức tạp.

Web Standard Support Gap

Các feature mới như Container Queries (CSS), @layer, Web Bluetooth, FileSystem Access API có mức support khác nhau giữa 3 engine ở cùng thời điểm. Test matrix giúp phát hiện feature nào dùng nhưng chưa support đủ.

Không phải mọi test suite đều cần chạy full 3 browser liên tục. Mục tiêu là chạy đúng scope, đúng thời điểm — phần tiếp theo sẽ đề cập chiến lược này.

3

Setup Projects 3 Engine

Config cơ bản nhất — 3 project, mỗi cái dùng 1 engine:

// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] },
    },
    {
      name: 'webkit',
      use: { ...devices['Desktop Safari'] },
    },
  ],
});

Mỗi project kế thừa toàn bộ use defaults từ device preset — viewport, userAgent, locale. Tên project ('chromium', 'firefox', 'webkit') là giá trị được dùng với CLI flag --project và là giá trị của fixture projectName.

Lưu ý quan trọng về devices preset:

  • devices['Desktop Chrome'] set channel: undefined — dùng Chromium bundled của Playwright, không phải Chrome thật trên máy user.
  • Preset chỉ define viewport, userAgent, và một số defaults — không lock cứng engine. Engine được quyết định bởi field channel (xem mục 4).
  • Có thể override bất kỳ field nào trong preset sau khi spread.
// Override viewport sau khi spread preset
{
  name: 'webkit-wide',
  use: {
    ...devices['Desktop Safari'],
    viewport: { width: 1440, height: 900 }, // override default 1280x720
  },
},
4

Branded Browsers — Chrome Stable và Edge

Chromium bundled của Playwright khác Chrome stable user cài: không có Google services, không Widevine, không codec H.264/H.265 proprietary. Nếu muốn test trên Chrome thật hoặc Edge, dùng channel:

projects: [
  {
    name: 'chrome-stable',  // Chrome thật, không Chromium bundled
    use: { ...devices['Desktop Chrome'], channel: 'chrome' },
  },
  {
    name: 'edge',
    use: { ...devices['Desktop Edge'], channel: 'msedge' },
  },
],

Khi dùng channel: 'chrome', Playwright tìm Chrome installed trên hệ thống theo path mặc định (Windows: Program Files/Google/Chrome, macOS: /Applications/Google Chrome.app). Nếu không tìm thấy, launch fail với error rõ ràng.

Trường hợp nên test Chrome stable:

  • App dùng Google Sign-In, Google Cast, Widevine DRM — những feature không có trong Chromium bundled.
  • Test extension Chrome (--load-extension flag) — Chromium bundled hỗ trợ, nhưng Chrome stable dùng để verify production behavior.
  • Bug chỉ reproduced trên Chrome stable version cụ thể — Chromium bundled của Playwright thường là recent Chromium tip-of-tree.

Kết hợp matrix đủ loại (ví dụ cho app media streaming):

projects: [
  // Chạy mọi PR
  { name: 'chromium', use: { ...devices['Desktop Chrome'] } },

  // Chạy trước release
  { name: 'firefox',  use: { ...devices['Desktop Firefox'] } },
  { name: 'webkit',   use: { ...devices['Desktop Safari'] } },

  // Chạy khi có thay đổi liên quan DRM/codec
  { name: 'chrome-stable', use: { ...devices['Desktop Chrome'], channel: 'chrome' } },
],
5

Chạy Theo Filter Project

Khi đã config nhiều projects, CLI cho phép filter để chỉ chạy một subset:

# Chạy tất cả projects (cross-browser full)
npx playwright test

# Chỉ Chromium — dev local nhanh
npx playwright test --project=chromium

# Chỉ WebKit — debug bug Safari
npx playwright test --project=webkit

# Nhiều project cùng lúc
npx playwright test --project=chromium --project=firefox

# Wildcard — chạy tất cả project có "chrome" trong tên
npx playwright test --project="*chrome*"

Flag --project là match theo tên project (string exact hoặc glob pattern). Nếu không match project nào, Playwright báo error thay vì chạy tất cả.

Trong package.json scripts, thường setup sẵn alias:

{
  "scripts": {
    "test":        "playwright test",
    "test:chrome": "playwright test --project=chromium",
    "test:webkit": "playwright test --project=webkit",
    "test:firefox": "playwright test --project=firefox",
    "test:cross":  "playwright test --project=chromium --project=firefox --project=webkit"
  }
}
6

Chiến Lược Smoke + Full Nightly

Chạy full 3-browser matrix trên mỗi PR tốn 3× CI time. Với test suite lớn (200+ test), điều này ảnh hưởng trực tiếp đến developer feedback loop. Pattern phổ biến là tách smoke (PR fast) và full cross-browser (nightly hoặc trước release):

// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  projects: [
    // Smoke — chỉ Chromium, chỉ critical path
    // Dùng cho CI PR check — chạy nhanh
    {
      name: 'smoke',
      testMatch: /.*smoke.*\.spec\.ts/,
      use: { ...devices['Desktop Chrome'] },
    },

    // Cross-browser full — chạy nightly hoặc trước release
    {
      name: 'chromium',
      testIgnore: /.*smoke.*\.spec\.ts/, // bỏ qua smoke (đã chạy ở project trên)
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'firefox',
      testIgnore: /.*smoke.*\.spec\.ts/,
      use: { ...devices['Desktop Firefox'] },
    },
    {
      name: 'webkit',
      testIgnore: /.*smoke.*\.spec\.ts/,
      use: { ...devices['Desktop Safari'] },
    },
  ],
});

CI pipeline sau đó chọn project theo context:

# CI trên PR — chỉ smoke Chromium
npx playwright test --project=smoke

# CI nightly job — full matrix
npx playwright test --project=chromium --project=firefox --project=webkit

Cách khác là dùng environment variable để điều kiện hóa:

// playwright.config.ts
const isCI     = !!process.env.CI;
const isNightly = process.env.NIGHTLY === 'true';

export default defineConfig({
  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },

    // Firefox và WebKit chỉ chạy trên nightly CI
    ...(isNightly ? [
      { name: 'firefox', use: { ...devices['Desktop Firefox'] } },
      { name: 'webkit',  use: { ...devices['Desktop Safari'] } },
    ] : []),
  ],
});

Ưu điểm của approach env variable: config đơn giản hơn, không cần quản lý testMatch/testIgnore riêng. Nhược điểm: smoke và full share cùng test files, khó tách.

7

Conditional Skip Per Browser

Một số test cover feature chưa support trên engine cụ thể, hoặc có known bug chưa fix. Dùng test.skip() với browserName fixture để skip có điều kiện:

test('PDF download', async ({ page, browserName }) => {
  test.skip(browserName === 'webkit', 'PDF download không hoạt động trên WebKit, xem WK-123');
  // Phần còn lại chỉ chạy trên Chromium và Firefox
  await page.click('[data-testid="download-pdf"]');
  const download = await page.waitForEvent('download');
  expect(download.suggestedFilename()).toBe('invoice.pdf');
});

Syntax test.skip(condition, reason):

  • condition là boolean — evaluate tại runtime khi test bắt đầu.
  • reason là string — hiển thị trong report, giúp review sau khi skip.
  • Test bị skip sẽ có status skipped trong HTML report — không phải passed hay failed.

Ngoài browserName, còn có thể dùng platform để skip:

test('clipboard API', async ({ page, browserName }) => {
  // WebKit trên Linux có vấn đề với clipboard API
  test.skip(
    browserName === 'webkit' && process.platform === 'linux',
    'WebKit Linux không support Clipboard API đầy đủ'
  );
  // ...
});

Dùng test.fixme() thay vì test.skip() khi bug đã report và có ticket theo dõi — test vẫn chạy nhưng nếu pass thì được đánh dấu "unexpected pass" (nhắc nhở remove annotation khi fix xong):

test('Web Share API', async ({ page, browserName }) => {
  test.fixme(browserName === 'firefox', 'Firefox Web Share chưa stable, ticket PLW-456');
  // ...
});
8

Skip Cả describe Block

Khi nhiều test trong cùng group đều cần skip trên một browser, dùng test.skip() dạng callback ở cấp describe để tránh lặp annotation:

test.describe('PDF feature', () => {
  // Skip toàn bộ describe block trên WebKit
  test.skip(({ browserName }) => browserName === 'webkit', 'WebKit không support PDF viewer');

  test('view PDF inline', async ({ page }) => {
    await page.goto('/documents/sample.pdf');
    await expect(page.locator('.pdf-viewer')).toBeVisible();
  });

  test('download PDF', async ({ page }) => {
    await page.click('[data-testid="download-pdf"]');
    const download = await page.waitForEvent('download');
    expect(download.suggestedFilename()).toMatch(/\.pdf$/);
  });

  test('print PDF', async ({ page }) => {
    // ...
  });
});

Callback form nhận { browserName, ... } tương tự như fixture destructuring — evaluate tại runtime trước khi mỗi test trong block bắt đầu.

Một biến thể khác là dùng test.describe.skip() để skip cứng (không conditional). Không nên dùng vì không rõ lý do và không có way để tự động "unseen" khi browser fix:

// Nên tránh — không có reason và không conditional
test.describe.skip('PDF feature', () => { /* ... */ });

// Ưu tiên — có reason, có điều kiện
test.describe('PDF feature', () => {
  test.skip(({ browserName }) => browserName === 'webkit', 'WebKit PDF not supported');
  // ...
});
9

Visual Snapshot Riêng Từng Engine

Mỗi engine render font, shadow, anti-aliasing, và sub-pixel khác nhau. Nếu dùng toHaveScreenshot() với multi-browser matrix, Playwright tự tạo snapshot riêng cho từng project để tránh conflict:

test('hero section render', async ({ page }) => {
  await page.goto('/');
  await expect(page).toHaveScreenshot('hero.png');
});

Playwright lưu snapshot vào thư mục với tên project embedded:

tests/
  home.spec.ts
  home.spec.ts-snapshots/
    hero-chromium-linux.png    ← snapshot của project 'chromium' trên Linux
    hero-firefox-linux.png     ← snapshot của project 'firefox' trên Linux
    hero-webkit-linux.png      ← snapshot của project 'webkit' trên Linux
    hero-chromium-darwin.png   ← snapshot của project 'chromium' trên macOS

Pattern đặt tên mặc định: {testName}-{projectName}-{platform}{n}.png. Update snapshot:

# Update tất cả snapshots
npx playwright test --update-snapshots

# Update chỉ 1 project
npx playwright test --project=webkit --update-snapshots

Một số điểm cần lưu ý với visual testing multi-browser:

  • Font rendering trên Linux khác macOS — nếu CI chạy Linux mà dev update snapshot trên macOS, snapshot sẽ mismatch khi CI chạy. Nên update snapshot trên cùng OS với CI (hoặc dùng Docker).
  • Threshold maxDiffPixels hoặc maxDiffPixelRatio nên set cao hơn cho Firefox và WebKit so với Chromium — do render khác nhau nhiều hơn.
  • Animate / transition cần animations: 'disabled' trong fixture để snapshot ổn định.
// playwright.config.ts — threshold khác nhau per project
projects: [
  {
    name: 'chromium',
    use: {
      ...devices['Desktop Chrome'],
      // Chromium: threshold thấp — render stable
    },
  },
  {
    name: 'firefox',
    use: {
      ...devices['Desktop Firefox'],
    },
  },
  {
    name: 'webkit',
    use: {
      ...devices['Desktop Safari'],
    },
  },
],
// Snapshot threshold global — tăng nếu cần
expect: {
  toHaveScreenshot: { maxDiffPixelRatio: 0.02 },
},

Nếu cần threshold khác per test, override tại call site:

await expect(page).toHaveScreenshot('hero.png', {
  maxDiffPixels: 100,      // số pixel khác tuyệt đối
  // hoặc
  maxDiffPixelRatio: 0.05, // 5% pixel được phép khác
});
10

Performance Comparison

Mỗi engine có chi phí launch và action time khác nhau — ảnh hưởng trực tiếp đến tổng CI time khi chạy matrix:

Engine Launch time (approx) Action speed Ghi chú
Chromium ~500ms Nhanh nhất CDP native, overhead thấp
WebKit ~700ms Tùy OS macOS nhanh hơn Linux đáng kể
Firefox ~1s Chậm hơn Chromium CDP partial — overhead cao hơn

Các con số trên là ước tính trong điều kiện CI Linux. Trên macOS local, Firefox và WebKit nhanh hơn đáng kể. Trên CI có resource limit (2 CPU, 7GB RAM), sự chênh lệch lớn hơn.

Tổng CI time với 100 test:

  • Chỉ Chromium: ~100 test × overhead Chromium = baseline.
  • Chromium + Firefox + WebKit với workers: 4: khoảng 2.5–3× baseline (vì 3 project chạy song song nhưng bị giới hạn worker).
  • Nếu CI có đủ worker để chạy 3 project parallel: xấp xỉ 1.1–1.3× baseline.
11

WebKit Linux — Limitations Thực Tế

WebKit bundled của Playwright trên Linux là binary đã được Playwright team patch để chạy outside của macOS. Đây không phải Safari thật và có một số hành vi khác:

WebKit ≠ Safari iOS

WebKit bundled là engine Safari macOS, không phải Safari iOS. Behavior một số feature (đặc biệt touch events, viewport meta, safe-area-inset) sẽ khác. Nếu cần test iOS Safari chính xác, cần kết hợp với device emulation hoặc real device cloud.

WebKit Linux Flaky

WebKit trên Linux phụ thuộc nhiều vào system libraries (GTK, fontconfig, ICU). CI environment thiếu library hoặc khác version có thể gây flaky test — test pass locally trên macOS nhưng fail trên CI Ubuntu:

# Cài đủ system dependencies trên CI Linux
npx playwright install --with-deps webkit

API Không Support

Một số Web API mới chưa available trong WebKit bundled của Playwright:

  • Web Bluetooth API — không support.
  • Web USB — không support.
  • Web NFC — không support.
  • Một số navigator.mediaDevices API có thể behave khác.

Version Pinned

WebKit version được pin theo release Playwright. Playwright v1.44 bundle WebKit 17.4, v1.48 bundle WebKit 18.2. Không thể chọn WebKit version khác ngoài việc đổi Playwright version — đây là design decision của team Playwright để đảm bảo reproducibility.

// Kiểm tra WebKit version trong test nếu cần
test('check engine version', async ({ browser, browserName }) => {
  test.skip(browserName !== 'webkit');
  // browser.version() trả về version string của engine
  const version = browser.version();
  console.log('WebKit version:', version);
});
12

Firefox — Những Điểm Khác Biệt

CDP Partial

Firefox không native support CDP. Playwright dùng một bridge protocol riêng (Firefox Remote Debugging Protocol) và emulate CDP trên đó. Một số CDP method không available trên Firefox — đặc biệt là:

  • CDPSession — một số command không hoạt động trên Firefox.
  • Coverage API (coverage.startJSCoverage()) — không support.
  • Performance timeline via CDP — limited.

Test dùng page.context().newCDPSession() cần skip trên Firefox:

test('collect JS coverage', async ({ page, browserName }) => {
  test.skip(browserName !== 'chromium', 'Coverage API chỉ support Chromium');
  // ...
});

firefoxUserPrefs

Firefox có thể set preference riêng qua firefoxUserPrefs trong launch options — tương đương about:config:

projects: [
  {
    name: 'firefox',
    use: {
      ...devices['Desktop Firefox'],
      launchOptions: {
        firefoxUserPrefs: {
          // Tắt confirm download dialog trong Firefox
          'browser.download.useDownloadDir': true,
          'browser.download.folderList': 2,
          // Enable Web Notifications không cần permission prompt
          'dom.webnotifications.enabled': false,
        },
      },
    },
  },
],

Geolocation Behavior

Firefox yêu cầu permission grant theo cách khác Chrome. Nếu test dùng geolocation, cần set permission trước:

// Cần grant geolocation permission cho Firefox
const context = await browser.newContext({
  permissions: ['geolocation'],
  geolocation: { longitude: 106.6833, latitude: 10.8231 },
});
// Trên Chromium, permissions thường implicit hơn
13

CI Parallelization Trade-off

Chạy 3 projects với N test file = 3N test units cần schedule. Với workers: 4 và 60 test files:

Chromium: 60 test × 1 = 60 units
Firefox:  60 test × 1 = 60 units
WebKit:   60 test × 1 = 60 units
-------------------------------------
Tổng: 180 units, workers: 4
Thời gian ≈ 3× so với chạy chỉ Chromium

Để giảm CI time khi cần full matrix, kết hợp với sharding (bài A.7):

# Shard 1/3 — chạy trên CI job 1
npx playwright test --shard=1/3

# Shard 2/3 — chạy trên CI job 2
npx playwright test --shard=2/3

# Shard 3/3 — chạy trên CI job 3
npx playwright test --shard=3/3

Sharding chia theo test file, không theo project. Mỗi shard vẫn chạy tất cả 3 projects cho test files của nó. Tổng job cần = số shard, tổng CI time với 3 shard ≈ 1× baseline thay vì 3×.

Trade-off cần cân nhắc khi setup matrix:

  • 3 browser binary install: mỗi binary ~150–200MB. CI cache browser binaries theo Playwright version để tránh download lại mỗi run.
  • WebKit Linux flaky: có thể làm CI fail mysterious. Cần monitor flakiness riêng cho WebKit project.
  • Firefox slower: nếu CI có hard timeout, test dài trên Firefox có thể bị kill trước khi xong.

Cache browser binaries trong GitHub Actions:

# .github/workflows/playwright.yml
- uses: actions/cache@v4
  with:
    path: ~/.cache/ms-playwright
    key: playwright-${{ hashFiles('package-lock.json') }}

- run: npx playwright install --with-deps
14

4 Pitfalls

Pitfall 1: Test Rely CDP-only API — Fail Silently Trên Firefox/WebKit

Test dùng Coverage API, CDPSession, hoặc Chrome-specific DevTools command sẽ throw error trên Firefox và WebKit thay vì skip rõ ràng:

// SAI — không có guard
test('collect coverage', async ({ page }) => {
  const coverage = await page.coverage.startJSCoverage(); // ← chỉ Chromium
  // ...
});

// ĐÚNG — guard tường minh
test('collect coverage', async ({ page, browserName }) => {
  test.skip(browserName !== 'chromium', 'Coverage API chỉ có trên Chromium');
  const coverage = await page.coverage.startJSCoverage();
  // ...
});

Pitfall 2: Visual Snapshot Không Separate Per Browser

Nếu đặt tên snapshot hardcode không dựa vào project name, snapshot của project sau sẽ overwrite snapshot của project trước:

// SAI — tên cứng, 3 project share 1 snapshot
await expect(page).toHaveScreenshot({ path: 'screenshots/hero.png' });

// ĐÚNG — để Playwright tự generate tên per project
await expect(page).toHaveScreenshot('hero.png');
// → tự sinh: hero-chromium-linux.png, hero-firefox-linux.png, ...

Pitfall 3: Channel Chrome Không Cài Trên CI

Config channel: 'chrome' tìm Chrome installed trên hệ thống. CI runner (Ubuntu, Alpine) không có Chrome sẵn — launch fail ngay:

Error: Failed to launch chromium because executable doesn't exist at
/usr/bin/google-chrome-stable

Fix: cài Chrome trên CI trước khi chạy, hoặc chỉ enable project channel chrome khi chạy local / trên runner đã có Chrome:

// Chỉ include chrome-stable project khi không phải CI
...(process.env.CI ? [] : [
  { name: 'chrome-stable', use: { ...devices['Desktop Chrome'], channel: 'chrome' } },
]),

Pitfall 4: WebKit Linux Flaky — Mysterious CI Failure

WebKit trên Linux có thể fail do thiếu system font, ICU data mismatch, hoặc Xvfb config không đúng trong headless mode. Triệu chứng: test pass local (macOS) nhưng fail hoặc timeout trên CI Ubuntu với lỗi không rõ:

Error: browserType.launch: Target page, context or browser has been closed
  # Hoặc: Process crashed

Giải pháp:

  • Chạy npx playwright install --with-deps webkit trên CI để cài đủ system dependencies.
  • Nếu CI không support Xvfb, dùng headless: true tường minh trong project webkit config.
  • Tách WebKit ra CI job riêng với retry: 2 để phân biệt flaky và real fail.
15

Quiz + Bài Tiếp

Quiz

1. Config có 3 project: chromium, firefox, webkit. Lệnh npx playwright test --project=chromium --project=firefox sẽ chạy bao nhiêu project? Project webkit có chạy không?

Đáp án

Chạy 2 project: chromium và firefox. WebKit không chạy vì không được chỉ định trong flag --project. Khi dùng --project, chỉ các project được liệt kê mới chạy — không phải "all except".

2. Tại sao không nên dùng channel: 'chrome' trên CI runner Ubuntu mặc định mà không cài Chrome thêm?

Đáp án

CI runner Ubuntu mặc định không có Chrome installed. channel: 'chrome' yêu cầu Chrome binary có sẵn trên hệ thống — Playwright không tự download Chrome stable. Launch sẽ fail với error "executable doesn't exist". Cần cài Chrome trên CI hoặc disable project này khi chạy trên CI.

3. Test dùng await expect(page).toHaveScreenshot('hero.png') chạy trên 3 project: chromium, firefox, webkit. Playwright tạo bao nhiêu snapshot file? Tên file là gì?

Đáp án

Playwright tạo 3 snapshot file riêng biệt, tên tự sinh theo pattern {testName}-{projectName}-{platform}.png. Ví dụ trên Linux: hero-chromium-linux.png, hero-firefox-linux.png, hero-webkit-linux.png. Mỗi project so sánh với snapshot của chính nó — không conflict.

4. Test gọi page.coverage.startJSCoverage() không có test.skip() guard nào. Chuyện gì xảy ra khi chạy test này trên project firefox?

Đáp án

Test sẽ throw error và fail vì Coverage API không support trên Firefox. Playwright không tự động skip — coverage API trả về null hoặc throw tuỳ implementation, dẫn đến test fail với error không rõ ràng. Cần thêm test.skip(browserName !== 'chromium', 'reason') để xử lý đúng.

5. Chiến lược smoke + full nightly có ưu điểm gì so với chạy full 3-browser trên mỗi PR?

Đáp án

Smoke + nightly giảm CI time trên PR (chỉ Chromium smoke nhanh) → feedback loop nhanh hơn cho developer. Full 3-browser chạy nightly hoặc trước release vẫn đảm bảo cross-browser coverage. Trade-off: cross-browser bug có thể không bị catch ngay trên PR, chỉ phát hiện sau trong nightly run — phù hợp với team chấp nhận delay nhỏ để đổi lấy CI speed.

Bài Tiếp Theo

Bài 55: Mobile Projects — Devices, Touch & Viewport Emulation