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.storage và chrome.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, offscreen và background 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.