CI/CD: Playwright E2E
CI/CD Pipeline: Automated E2E Testing for Chrome Extensions
Section titled “CI/CD Pipeline: Automated E2E Testing for Chrome Extensions”This log serves as a personal reference for the CI/CD pipeline architecture at Elie Labs, documenting the automated End-to-End (E2E) testing workflow for our Chrome extensions to ensure zero-downtime deployments.
1. Dependency Installation (Self-Correction on playwright-crx)
Section titled “1. Dependency Installation (Self-Correction on playwright-crx)”Initially, I considered using third-party wrappers like playwright-crx. However, to keep the tech stack lean and minimize dependency risks, I corrected this approach. Standard Playwright natively supports Chrome extensions, making external wrappers unnecessary.
I only need the official package:
npm install -D @playwright/test2. Playwright Configuration & Custom Test Script
Section titled “2. Playwright Configuration & Custom Test Script”Since Chrome extensions only run in Chromium-based browsers, testing them on Firefox or WebKit is redundant and will cause failures.
Playwright Config Update (playwright.config.ts): Disabled Firefox and WebKit entirely to focus testing cycles exclusively on Chromium.
Custom Extension Script (tests/extension.spec.ts): To test an extension, Playwright requires a persistent context to load the unpacked directory.
import { test as base, chromium, type BrowserContext } from '@playwright/test';import path from 'path';
// Define the path to the built extension (e.g., dist folder)const extensionPath = path.join(__dirname, '../dist');
export const test = base.extend<{ context: BrowserContext; extensionId: string;}>({ context: async ({ }, use) => { const pathToExtension = extensionPath; const context = await chromium.launchPersistentContext('', { headless: false, // Extensions require headful mode args: [ `--disable-extensions-except=${pathToExtension}`, `--load-extension=${pathToExtension}`, ], }); await use(context); await context.close(); }, extensionId: async ({ context }, use) => { // Dynamically extract the extension ID from the Service Worker let [background] = context.serviceWorkers(); if (!background) background = await context.waitForEvent('serviceworker'); const extensionId = background.url().split('/')[2]; await use(extensionId); },});
test('Popup page loads successfully', async ({ page, extensionId }) => { await page.goto(`chrome-extension://${extensionId}/popup.html`); // Add specific assertions for the UI here});3. Defining GitHub Actions Triggers
Section titled “3. Defining GitHub Actions Triggers”To ensure code stability before deployment and maintain daily health checks, I configured the workflow (.github/workflows/e2e.yml) with dual triggers.
Because I operate in the CST (UTC+8) timezone, a daily trigger at 08:00 AM CST translates to 00:00 UTC in standard cron syntax.
on: push: branches: [ "main" ] schedule: - cron: '0 0 * * *' # Runs daily at 00:00 UTC (08:00 CST)4. Automated Email Reporting via GitHub Actions
Section titled “4. Automated Email Reporting via GitHub Actions”To stay informed without constantly checking the repository, I integrated a step to zip the Playwright HTML report and email it directly using a Google App Password.
I set up two repository secrets in GitHub: MAIL_USERNAME (the Elie Labs support email) and MAIL_PASSWORD (the generated Google App Password).
- name: Compress Playwright Report if: always() run: zip -r playwright-report.zip playwright-report/
- name: Send Email Notification if: always() uses: dawidd6/action-send-mail@v3 with: server_address: smtp.gmail.com server_port: 465 username: ${{ secrets.MAIL_USERNAME }} password: ${{ secrets.MAIL_PASSWORD }} subject: E2E Test Report (${{ job.status }}) to: your-target-email@example.com # Can be any valid address from: GitHub Actions CI body: Automated testing completed! Triggered by ${{ github.event_name }}. Please find the attached report. attachments: playwright-report.zip🚨 Critical Reminders (Self-Correction)
Section titled “🚨 Critical Reminders (Self-Correction)”- The Headless Mode Dilemma (xvfb-run): Chrome extensions cannot be loaded in standard headless mode (
headless: true). However, GitHub Actions Ubuntu runners do not have physical displays. To bypass this, I must use Xvfb (X virtual framebuffer) to fake a display server. The test command in the YAML file must be prefixed like this:run: xvfb-run npx playwright test - Google App Passwords: Since standard password authentication for Gmail is disabled, a dedicated “App Password” must be generated from the Google Account security settings specifically for the GitHub Action to authenticate successfully.