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();
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')}`);
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}`);
}
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();
}
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');
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"
}
}
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.
Top comments (0)