DEV Community

Weather Clock Dash
Weather Clock Dash

Posted on

How I Automated Firefox Extension Screenshots for AMO Listings

How I Automated Firefox Extension Screenshots for AMO Listings

One of the most tedious parts of publishing to Mozilla's Add-On Observatory (AMO) is creating consistent, high-quality screenshots. After publishing the Weather & Clock Dashboard extension, I wanted a repeatable process for capturing screenshots automatically.

The Challenge

AMO requires screenshots in specific dimensions (typically 1280×800 or similar), and they need to look polished — not rushed screenshots taken on whatever monitor you happen to be using.

The Solution: Playwright for Automated Screenshots

Playwright (or Puppeteer) makes this trivially easy once you have the right setup:

const { chromium } = require('playwright');
const path = require('path');

async function captureExtensionScreenshots() {
  const browser = await chromium.launch();
  const context = await browser.newContext({
    viewport: { width: 1280, height: 800 }
  });

  // Load your extension
  // For a new tab extension, navigate to the extension's HTML directly
  const page = await context.newPage();

  // Navigate to your extension's newtab.html
  // When loaded as extension, use chrome-extension://[id]/newtab.html
  // For screenshots, you can load the HTML file directly
  await page.goto(`file://${path.resolve('./newtab.html')}`);

  // Wait for any async data to load (weather, etc.)
  await page.waitForTimeout(2000);

  // Take full page screenshot
  await page.screenshot({
    path: './screenshots/main-view.png',
    fullPage: false  // viewport only for consistent sizing
  });

  // Screenshot dark mode
  await page.evaluate(() => {
    document.body.classList.add('dark-mode');
  });
  await page.waitForTimeout(500);
  await page.screenshot({ path: './screenshots/dark-mode.png' });

  await browser.close();
}

captureExtensionScreenshots();
Enter fullscreen mode Exit fullscreen mode

Mocking the Weather API

For screenshots, you don't want to hit a real weather API — it's unreliable and your test city might have ugly weather. Mock it:

// Before navigating, intercept the API call
await page.route('**/api.openweathermap.org/**', route => {
  route.fulfill({
    status: 200,
    contentType: 'application/json',
    body: JSON.stringify({
      weather: [{ main: 'Clear', description: 'clear sky', icon: '01d' }],
      main: { temp: 72, feels_like: 70, humidity: 45 },
      name: 'San Francisco',
      sys: { country: 'US' }
    })
  });
});

await page.goto(`file://${path.resolve('./newtab.html')}`);
Enter fullscreen mode Exit fullscreen mode

Now your screenshots always show perfect sunny weather in San Francisco — great for marketing!

Setting Up Multiple Scenarios

const scenarios = [
  { name: 'light-mode', theme: 'light', city: 'New York' },
  { name: 'dark-mode', theme: 'dark', city: 'Tokyo' },
  { name: 'world-clocks', theme: 'light', city: 'London', showClocks: true },
];

for (const scenario of scenarios) {
  await page.evaluate((s) => {
    localStorage.setItem('theme', s.theme);
    localStorage.setItem('city', s.city);
    if (s.showClocks) localStorage.setItem('showWorldClocks', 'true');
  }, scenario);

  await page.reload();
  await page.waitForTimeout(1500);
  await page.screenshot({ path: `./screenshots/${scenario.name}.png` });
  console.log(`Captured: ${scenario.name}`);
}
Enter fullscreen mode Exit fullscreen mode

Adding Proper Dimensions for Different Platforms

Different platforms have different screenshot requirements:

const platforms = {
  amo: { width: 1280, height: 800 },       // AMO preferred
  chrome: { width: 1280, height: 800 },     // Chrome Web Store
  productHunt: { width: 1270, height: 952 } // Product Hunt
};

for (const [platform, viewport] of Object.entries(platforms)) {
  const ctx = await browser.newContext({ viewport });
  const p = await ctx.newPage();
  // ... setup and navigate
  await p.screenshot({ path: `./screenshots/${platform}.png` });
  await ctx.close();
}
Enter fullscreen mode Exit fullscreen mode

Adding a Border/Frame

AMO screenshots look more polished with a subtle border or device frame. You can use Sharp (Node.js image library) to add post-processing:

const sharp = require('sharp');

// Add a border
await sharp('./screenshots/main-view.png')
  .extend({
    top: 2, bottom: 2, left: 2, right: 2,
    background: { r: 200, g: 200, b: 200, alpha: 1 }
  })
  .toFile('./screenshots/main-view-framed.png');
Enter fullscreen mode Exit fullscreen mode

Making It Part of Your Build Process

I added this to package.json:

{
  "scripts": {
    "screenshots": "node scripts/capture-screenshots.js",
    "build": "npm run screenshots && npm run package"
  }
}
Enter fullscreen mode Exit fullscreen mode

Now every release automatically has fresh, consistent screenshots.

Results

For the Weather & Clock Dashboard, this approach generated 5 polished screenshots in under 30 seconds. They're consistent across releases, and I can regenerate them any time the UI changes.

The extension is live on AMO: Weather & Clock Dashboard

Have you automated your extension screenshots? Share your approach in the comments!


This is part of a series on building and publishing Firefox browser extensions. Follow along for more practical tips.

firefox #browserextension #webdev #playwright #automation

Top comments (0)