Setup for Testing Chrome Extensions with Playwright
Tags: playwright, testing, learning, programming
Published: 2024-02-12 15:19:57

Hey everyone! 🚀 If you're diving into the world of Chrome extension testing, you've probably come across a few recommended libraries - Playwright, Selenium, and WebDriverIO. I've had my share of adventures with Selenium, Cypress, TestCafé, and Nightwatch.js, but never penned down my experiences. This time, with Playwright catching my eye (thanks to its growing buzz in the tech community), I thought, "Why not document this journey?"

## Why Playwright?

Choosing Playwright felt like stepping into somewhat familiar territory with a twist. It's not just another testing framework; it's designed to handle modern web apps' complexities, offering first-class support for Chrome extensions. The choice was spurred by its promising features and positive mentions in other projects. So, here we are!

## The Initial Setup Challenge

Setting up Playwright for Chrome extension testing was a different ball game compared to my routine web app tests. Typically, testing environments are URL-based (local.dev and test.dev!), but extensions require a more nuanced approach. [Playwright's setup guide for Chrome extensions](https://playwright.dev/docs/chrome-extensions) is a treasure trove of information, yet the shift in mindset from a URL hosted somewhere on the testing environment or locally to extension-focused testing took a minute to click. It's about making your extension code accessible to Playwright, setting the stage with `const pathToExtension` to get the ball rolling. 
```typescript
//from the code example on https://playwright.dev/docs/chrome-extensions
import { test as base, chromium, type BrowserContext } from '@playwright/test';
import path from 'path';

export const test = base.extend<{
  context: BrowserContext;
  extensionId: string;
}>({
  context: async ({ }, use) => {
    //path to the chrome extension
    const pathToExtension = path.join(__dirname, 'my-extension');
    const context = await chromium.launchPersistentContext('', {
      headless: false,
      args: [
        `--disable-extensions-except=${pathToExtension}`,
        `--load-extension=${pathToExtension}`,
      ],
    });
    await use(context);
    await context.close();
  },
  extensionId: async ({ context }, use) => {
    /*
    // for manifest v2:
    let [background] = context.backgroundPages()
    if (!background)
      background = await context.waitForEvent('backgroundpage')
    */

    // for manifest v3:
    let [background] = context.serviceWorkers();
    if (!background)
      background = await context.waitForEvent('serviceworker');

    const extensionId = background.url().split('/')[2];
    await use(extensionId);
  },
});
export const expect = test.expect;
```

Interestingly, I stuck with `headless: false`, the thought of trying out `headless: new` is on my radar, promising for future experiments and surely a story to tell.

## What's Next?
The plan is to keep this series bite-sized and digestible, aiming for a mix of tutorial, diary, and tech talk. Alongside the practicalities of Playwright, I'll be enlisting ChatGPT's help for language transitioning and solving tricky bugs. Expect to see how this AI tool transforms the way we tackle small coding challenges, especially when it comes to refining TypeScript skills and debugging.

Stay tuned for practical tips, CI/CD development setup, AI-enhanced coding tricks, and more from our Playwright exploration. Let's make this journey together! If you've been tinkering with Playwright or have your own extension testing tales, drop your thoughts below. Sharing is how we grow, and I'm all ears for your experiences and tips.