Why You Can't Test a Chrome Extension the Normal Way

English 简体中文 繁体中文 Tiếng Việt
Summary

Testing Chrome extensions presents unique challenges due to their architecture, where popups are browser chrome, service workers are ephemeral, and content scripts run in isolated JavaScript contexts. Traditional automation tools like Puppeteer and Playwright struggle with these distinctions, and the removal of the `--load-extension` flag from branded Chrome builds further complicates direct testing. A new approach involves using Chrome for Testing builds and leveraging the `manifest.json` to generate initial smoke tests, with tools like `pikabo` enabling targeted assertions across different extension components like the popup, worker, and content scripts. This allows for more robust testing by directly interacting with extension surfaces and capturing events, including downloads initiated from the worker, and provides a permission audit to ensure declared permissions are actually exercised.

Tôi đã xây dựng các tiện ích mở rộng trình duyệt được vài năm nay: PageSaver, chuyển đổi một trang web thành PDF hoặc hình ảnh, và một số tiện ích khác. Mỗi tiện ích đều được phát hành mà không có một bài kiểm tra tự động nào. Không phải vì tôi không muốn kiểm tra, mà vì các công cụ tôi đã biết không thể tiếp cận được những phần thực sự bị lỗi.

Vòng lặp luôn giống nhau. Thay đổi một tệp, mở chrome://extensions, nhấn tải lại, nhấp vào biểu tượng thanh công cụ, kiểm tra trang tùy chọn, sau đó mở ba cửa sổ DevTools riêng biệt vì cửa sổ bật lên, trang và service worker mỗi cái giữ bảng điều khiển riêng của chúng. Bỏ sót một cái và lỗi sẽ được phát hành.

Cuối cùng, tôi đã ngồi xuống để khắc phục điều này một cách đúng đắn, và điều đầu tiên tôi phải chấp nhận là vấn đề không phải là sự lười biếng trong hệ sinh thái. Kiểm thử một tiện ích mở rộng thực sự khác với kiểm thử một ứng dụng web, theo ba cách cụ thể.

Tiện ích mở rộng không phải là một trang

Playwright và Puppeteer rất tuyệt vời, và chúng điều khiển các trang. Một tiện ích mở rộng phần lớn không phải là một trang.

Bắt đầu với cửa sổ bật lên. Khi bạn nhấp vào biểu tượng thanh công cụ của tiện ích mở rộng, thứ mở ra không phải là một tab — đó là giao diện người dùng của trình duyệt, được hiển thị bởi trình duyệt bên ngoài hệ thống phân cấp trang mà tự động hóa biết cách truy cập. Không có tab nào để gắn vào và không có tay cầm nào để nắm lấy. Bạn không thể nhấp vào nó từ một tập lệnh, và không có thủ thuật chọn lọc nào có thể thay đổi điều đó.

Phần nền còn tệ hơn. Theo Manifest V3, đó là một service worker, nghĩa là không có tab, không có DOM và không có cửa sổ. page.evaluate() không có gì để gắn vào, vì không có trang nào. Worker cũng tắt khi nó không hoạt động và khởi động lại vào sự kiện tiếp theo, vì vậy ngay cả câu hỏi "nó có đang chạy không" cũng đi kèm với một dấu thời gian.

Content script trông giống như trường hợp dễ dàng và không phải vậy. Chúng chạy trong một thế giới cô lập: cùng DOM với trang, ngữ cảnh JavaScript hoàn toàn riêng biệt. Nếu content script của bạn đặt một biến và bạn đánh giá một biểu thức trong trang để đọc nó, bạn sẽ nhận được undefined. Hai ngữ cảnh chia sẻ đánh dấu và không gì khác.

Vì vậy, cửa sổ bật lên không thể nhấp, worker không thể đánh giá và content script không thể kiểm tra từ trang. Đó là phần lớn của một tiện ích mở rộng.

Sau đó Chrome đã gỡ bỏ cửa trước

Câu trả lời cũ cho tất cả những điều này là khởi chạy một trình duyệt với --load-extension và điều khiển nó. Điều đó đã ngừng hoạt động. Google đã xóa cờ trong Chrome 137, vì một lý do hợp lý được nêu ra: "nó thường bị lạm dụng để tải phần mềm độc hại và không mong muốn vào trình duyệt."

Phần quan trọng đối với việc kiểm thử nằm trong thông báo trên nhóm chromium-extensions:

Xin lưu ý rằng thay đổi này chỉ áp dụng cho các bản dựng có thương hiệu Chrome. --load-extension sẽ tiếp tục hoạt động như trước đây trong các bản dựng không có thương hiệu Chrome, chẳng hạn như Chromium và Chrome For Testing.

Câu đơn đó là toàn bộ chiến lược. Cờ không biến mất, nó biến mất khỏi Chrome có thương hiệu. Chrome for Testing — bản dựng mà Google xuất bản đặc biệt cho tự động hóa — vẫn tôn trọng nó, và Google đặt tên trực tiếp nó là nơi cần đến.

Có những bài đăng trên blog gợi ý rằng bạn có thể buộc Chrome có thương hiệu hoạt động trở lại với --disable-features=DisableLoadExtensionCommandLineSwitch. Đừng dựa vào điều đó. Tôi đã kiểm tra nó với Chrome 150.0.7871.187 trong khi làm việc này, và một tiện ích mở rộng chưa đóng gói không tải được có hoặc không có cờ. --enable-unsafe-extension-debugging cũng không mang nó trở lại. Lối thoát đó đã đóng, và nó chưa bao giờ được chấp thuận ngay từ đầu.

Lời khuyên của tôi là ngừng coi Chrome trong thanh tác vụ của bạn là trình duyệt kiểm thử. Hãy sử dụng Chrome for Testing. Đó là con đường được hỗ trợ, là con đường mà Google chỉ vào, và là con đường duy nhất sẽ vẫn hoạt động vào quý tới.

Lỗi bạn không thể nhìn thấy

Đây là điều đã thuyết phục tôi rằng điều này cần các công cụ thực sự thay vì một đống tập lệnh.

Bảng điều khiển của service worker MV3 chỉ tồn tại khi trình kiểm tra của nó được mở. Chrome không đệm đầu ra đó cho bạn. Vì vậy, nếu tập lệnh nền của bạn ném lỗi ngay dòng đầu tiên — một lỗi đánh máy, một lần nhập bị thiếu, một API mà bạn quên khai báo quyền truy cập — bạn mở cửa sổ bật lên, thấy một hình chữ nhật trống, kiểm tra bảng điều khiển trang, không tìm thấy gì, và không biết bất cứ điều gì đã ném lỗi cả. Worker đã chết trước khi bạn theo dõi.

Giải pháp là gắn vào qua giao thức DevTools trước khi worker bắt đầu, để đầu ra được đệm đã được thu thập khi nó ném lỗi. Làm điều đó, và cùng một lỗi sẽ tự báo cáo:

✗ service worker starts without errors
  assertNoConsoleErrors: 2 console error(s):
    [worker] ReferenceError: initialise is not defined
      at chrome-extension://abc…/background.js:4:1
    [worker] Error: Unhandled rejection: storage quota exceeded
      at chrome-extension://abc…/background.js:31:16

Tệp và dòng, từ một tập lệnh chưa bao giờ chạy đủ lâu để hiển thị cho bạn một dấu vết ngăn xếp bằng tay.

Hầu hết bộ kiểm thử đầu tiên đã có trong manifest của bạn

Đây là phần làm tôi ngạc nhiên. manifest.json đã khai báo cửa sổ bật lên, trang tùy chọn, các mẫu khớp content script của bạn và liệu bạn có service worker hay không. Điều đó đủ để tạo ra một bài kiểm tra khói mà không cần viết bất cứ điều gì.

Vì vậy, tôi đã xây dựng pikabo dựa trên ý tưởng đó. Trỏ nó vào một tiện ích mở rộng chưa đóng gói:

$ npx pikabo explore --ext ./my-extension

my-extension v2.1.0 · MV3
popup: popup.html · options: options.html · service worker · 2 content script blocks · 5 permissions

Generated 5 smoke test(s) → tests/smoke.generated.yaml
my-extension smoke tests
  ✓ service worker starts without errors      12ms
  ✓ popup renders                            431ms
  ✓ options page renders                     318ms
  ✓ content script injects on github.com     772ms
  ✓ content script injects on gitlab.com     684ms

  5 passed · 3.8s
  html      pikabo-results/report.html

Không có bài kiểm tra viết tay nào liên quan đến điều đó. Nó đọc manifest, mở từng bề mặt được khai báo trong một Chromium thực, kiểm tra xem cửa sổ bật lên có hiển thị nội dung thực tế thay vì một lớp vỏ trống hay không, điều hướng đến một URL khớp với từng mẫu content script để xác nhận việc chèn, và khẳng định worker đã khởi động sạch sẽ. Bộ kiểm tra được tạo được ghi vào đĩa dưới dạng YAML để bạn có thể tiếp tục chỉnh sửa nó thay vì coi nó như một hộp đen.

Từ đó, bạn viết các khẳng định có ý nghĩa, và phần hữu ích là mỗi bề mặt có thể được truy cập bằng tên:

name: PageSaver

tests:
  - name: popup saves a PDF
    steps:
      - navigate: https://example.com
      - openPopup
      - click: "#save-pdf"
      - waitForDownload: "*.pdf"
      - assertStorage:
          key: lastSave
          exists: true
      - assertNoConsoleErrors

in: worker chạy một bước bên trong service worker, nơi chrome.storagechrome.runtime tồn tại. in: popup truy cập cửa sổ bật lên như một tài liệu thực. Ngoài ra còn có sidepanel, devtools, offscreenbackground cho MV2. Khả năng nói bước nào của tiện ích mở rộng mà một bước tác động là điều còn thiếu.

Đối với một tiện ích mở rộng xuất tệp, các lượt tải xuống sẽ được chụp lại bất kể chúng đến từ đâu, bao gồm cả chrome.downloads.download() được gọi từ worker mà không có trang nào liên quan. Không có trang nào nhìn thấy nó, vì vậy tự động hóa cấp trang không thể. assertPdf sau đó đọc tệp: số trang, hình học, kích thước byte, nhà sản xuất.

Một lưu ý thiết lập, vì nó đã làm tôi gặp khó khăn ngay lập tức. Nếu tiện ích mở rộng của bạn có manifest.json ở thư mục gốc của kho lưu trữ, đừng npm install vào đó; node_modules sẽ nằm cạnh manifest của bạn và được đóng gói vào tệp ZIP bạn tải lên Web Store. Giữ các bài kiểm tra trong thư mục riêng của chúng và trỏ vào tiện ích mở rộng:

mkdir extension-tests && cd extension-tests
npm init -y && npm install --save-dev pikabo
npx playwright-core install chromium
npx pikabo run tests --ext ../my-extension

Nó sẽ không làm gì

Hãy rõ ràng về các giới hạn, vì khoảng cách giữa "bài kiểm tra vượt qua" và "nó hoạt động" là nơi niềm tin bị mất.

Các cài đặt .crx đã đóng gói từ Web Store không được hỗ trợ, chỉ có các thư mục chưa đóng gói. Giao diện người dùng của trình duyệt bên ngoài một tài liệu vẫn không thể nhấp — menu biểu tượng thanh công cụ, phần mở rộng hình mảnh ghép, lời nhắc cấp quyền gốc — mặc dù mọi thứ mà các bề mặt đó kích hoạt đều có thể truy cập được thông qua tài liệu cửa sổ bật lên, trang tùy chọn hoặc worker. Các phím tắt được khai báo trong manifest.commands không thể được gửi dưới dạng sự kiện phím thực, vì vậy bạn thay vào đó sẽ gửi trình xử lý thông qua worker. Và Chrome có thương hiệu thì không, theo tất cả những điều trên.

Ngoài ra còn có một kiểm toán quyền hạn chạy trên mỗi phiên, so sánh các không gian tên chrome.* mà mã của bạn thực sự chạm vào với những gì manifest khai báo. Nó báo cáo các phát hiện là không được quan sát thấy thay vì không sử dụng, điều này quan trọng: một quyền được thực thi chỉ trên một đường dẫn mà các bài kiểm tra của bạn không bao giờ đi qua sẽ trông giống hệt như một quyền bị chết. Một bộ kiểm tra khói mở cửa sổ bật lên không chứng minh điều gì về quyền hạn. Báo cáo nói như vậy thay vì cho phép bạn xóa thứ gì đó bạn cần.

pikabo là MIT, trên npm dưới tên pikabo, và yêu cầu Node 22 trở lên. Nếu bạn duy trì một tiện ích mở rộng và đã trì hoãn việc kiểm thử vì công cụ dường như không tồn tại, đây là khoảng trống cụ thể mà nó lấp đầy. Tôi thực sự muốn nghe những gì bị hỏng trên các tiện ích mở rộng không phải của tôi.

TESTING OPEN-SOURCE AUTOMATION CHROME-EXTENSION MANIFEST-V3 PLAYWRIGHT SERVICE-WORKER

  RELATED

  COMMENT

1
Anonymous
Aug 17, 2026 at 4:20 am
Hi there!