Table of contents

Can I use Headless Chromium to test web applications across different screen sizes?

Yes, Headless Chromium is an excellent tool for testing web applications across different screen sizes and viewport dimensions. It provides comprehensive capabilities for responsive web design testing, allowing developers to simulate various devices, screen resolutions, and orientations programmatically. This makes it invaluable for ensuring your web applications work consistently across desktop, tablet, and mobile devices.

Why Use Headless Chromium for Responsive Testing?

Headless Chromium offers several advantages for multi-screen testing:

  • Precise viewport control: Set exact pixel dimensions for any screen size
  • Device emulation: Simulate real devices with their specific characteristics
  • Screenshot comparison: Capture visual differences across screen sizes
  • Performance testing: Measure load times and rendering performance on different viewports
  • Automated testing: Integrate responsive tests into CI/CD pipelines
  • Cost-effective: No need for physical devices or cloud device farms

Setting Up Viewport Testing with Puppeteer

Puppeteer is the most popular Node.js library for controlling Headless Chromium. Here's how to set up basic viewport testing:

const puppeteer = require('puppeteer');

async function testMultipleViewports() {
  const browser = await puppeteer.launch({
    headless: true,
    args: ['--no-sandbox', '--disable-setuid-sandbox']
  });

  // Define common viewport sizes
  const viewports = [
    { width: 1920, height: 1080, name: 'Desktop Large' },
    { width: 1366, height: 768, name: 'Desktop Standard' },
    { width: 1024, height: 768, name: 'Tablet Landscape' },
    { width: 768, height: 1024, name: 'Tablet Portrait' },
    { width: 414, height: 896, name: 'iPhone XR' },
    { width: 375, height: 667, name: 'iPhone SE' },
    { width: 360, height: 640, name: 'Android Small' }
  ];

  const page = await browser.newPage();

  for (const viewport of viewports) {
    console.log(`Testing ${viewport.name} (${viewport.width}x${viewport.height})`);

    // Set viewport size
    await page.setViewport({
      width: viewport.width,
      height: viewport.height,
      deviceScaleFactor: 1
    });

    await page.goto('https://your-website.com', {
      waitUntil: 'networkidle2'
    });

    // Take screenshot
    await page.screenshot({
      path: `screenshots/${viewport.name.replace(' ', '_').toLowerCase()}.png`,
      fullPage: true
    });

    // Test specific elements visibility
    const navigationVisible = await page.evaluate(() => {
      const nav = document.querySelector('.main-navigation');
      return nav && window.getComputedStyle(nav).display !== 'none';
    });

    console.log(`Navigation visible: ${navigationVisible}`);
  }

  await browser.close();
}

testMultipleViewports();

Advanced Device Emulation

For more realistic testing, you can emulate specific devices with their exact characteristics:

const puppeteer = require('puppeteer');

async function testWithDeviceEmulation() {
  const browser = await puppeteer.launch({ headless: true });
  const page = await browser.newPage();

  // Emulate iPhone 12 Pro
  await page.emulate({
    viewport: {
      width: 390,
      height: 844,
      deviceScaleFactor: 3,
      isMobile: true,
      hasTouch: true,
      isLandscape: false
    },
    userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1'
  });

  await page.goto('https://your-website.com');

  // Test mobile-specific interactions
  await page.tap('#mobile-menu-button');
  await page.waitForSelector('.mobile-menu', { visible: true });

  await page.screenshot({ path: 'mobile-menu-test.png' });

  await browser.close();
}

Python Implementation with Selenium

For Python developers, Selenium provides similar capabilities:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import time

def test_responsive_design():
    # Configure Chrome options
    chrome_options = Options()
    chrome_options.add_argument('--headless')
    chrome_options.add_argument('--no-sandbox')
    chrome_options.add_argument('--disable-dev-shm-usage')

    driver = webdriver.Chrome(options=chrome_options)

    # Define viewport sizes to test
    screen_sizes = [
        (1920, 1080, 'Desktop Large'),
        (1366, 768, 'Desktop Standard'),
        (768, 1024, 'Tablet Portrait'),
        (375, 667, 'Mobile iPhone'),
        (360, 640, 'Mobile Android')
    ]

    try:
        for width, height, device_name in screen_sizes:
            print(f"Testing {device_name}: {width}x{height}")

            # Set window size
            driver.set_window_size(width, height)
            driver.get('https://your-website.com')

            # Wait for page to load
            time.sleep(2)

            # Take screenshot
            driver.save_screenshot(f'screenshots/{device_name.lower().replace(" ", "_")}.png')

            # Test responsive elements
            try:
                hamburger_menu = driver.find_element_by_css_selector('.hamburger-menu')
                is_mobile_menu_visible = hamburger_menu.is_displayed()
                print(f"Mobile menu visible: {is_mobile_menu_visible}")
            except:
                print("Mobile menu not found")

            # Test element positioning
            hero_section = driver.find_element_by_css_selector('.hero-section')
            hero_rect = driver.execute_script("""
                const element = arguments[0];
                const rect = element.getBoundingClientRect();
                return {
                    width: rect.width,
                    height: rect.height,
                    x: rect.x,
                    y: rect.y
                };
            """, hero_section)

            print(f"Hero section dimensions: {hero_rect}")

    finally:
        driver.quit()

test_responsive_design()

Comprehensive Testing Strategy

Here's a more comprehensive approach that includes visual regression testing and performance monitoring:

const puppeteer = require('puppeteer');
const pixelmatch = require('pixelmatch');
const PNG = require('pngjs').PNG;
const fs = require('fs');

class ResponsiveWebTester {
  constructor() {
    this.browser = null;
    this.baselineDir = './baseline_screenshots/';
    this.currentDir = './current_screenshots/';
    this.diffDir = './diff_screenshots/';
  }

  async initialize() {
    this.browser = await puppeteer.launch({
      headless: true,
      args: ['--no-sandbox', '--disable-setuid-sandbox']
    });

    // Ensure directories exist
    [this.baselineDir, this.currentDir, this.diffDir].forEach(dir => {
      if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
    });
  }

  async testResponsiveDesign(url, testCases) {
    const page = await this.browser.newPage();

    for (const testCase of testCases) {
      console.log(`Testing ${testCase.name}...`);

      // Configure viewport and device settings
      await page.setViewport({
        width: testCase.viewport.width,
        height: testCase.viewport.height,
        deviceScaleFactor: testCase.deviceScaleFactor || 1,
        isMobile: testCase.isMobile || false,
        hasTouch: testCase.hasTouch || false
      });

      if (testCase.userAgent) {
        await page.setUserAgent(testCase.userAgent);
      }

      // Navigate and wait for load
      await page.goto(url, { waitUntil: 'networkidle2' });

      // Run custom test functions
      if (testCase.customTests) {
        for (const test of testCase.customTests) {
          await test(page);
        }
      }

      // Performance metrics
      const metrics = await page.metrics();
      console.log(`${testCase.name} - Layout Duration: ${metrics.LayoutDuration}ms`);

      // Take screenshot
      const screenshotPath = `${this.currentDir}${testCase.name.replace(/\s+/g, '_').toLowerCase()}.png`;
      await page.screenshot({
        path: screenshotPath,
        fullPage: testCase.fullPage || false
      });

      // Compare with baseline if it exists
      await this.compareWithBaseline(testCase.name);
    }

    await page.close();
  }

  async compareWithBaseline(testName) {
    const fileName = `${testName.replace(/\s+/g, '_').toLowerCase()}.png`;
    const baselinePath = `${this.baselineDir}${fileName}`;
    const currentPath = `${this.currentDir}${fileName}`;
    const diffPath = `${this.diffDir}${fileName}`;

    if (!fs.existsSync(baselinePath)) {
      console.log(`No baseline found for ${testName}. Current screenshot will be used as baseline.`);
      fs.copyFileSync(currentPath, baselinePath);
      return;
    }

    const baseline = PNG.sync.read(fs.readFileSync(baselinePath));
    const current = PNG.sync.read(fs.readFileSync(currentPath));
    const { width, height } = baseline;
    const diff = new PNG({ width, height });

    const pixelDiff = pixelmatch(baseline.data, current.data, diff.data, width, height, {
      threshold: 0.1
    });

    if (pixelDiff > 0) {
      fs.writeFileSync(diffPath, PNG.sync.write(diff));
      console.log(`Visual differences detected in ${testName}: ${pixelDiff} pixels`);
    } else {
      console.log(`${testName}: No visual differences detected`);
    }
  }

  async close() {
    if (this.browser) {
      await this.browser.close();
    }
  }
}

// Usage example
async function runResponsiveTests() {
  const tester = new ResponsiveWebTester();
  await tester.initialize();

  const testCases = [
    {
      name: 'Desktop Large',
      viewport: { width: 1920, height: 1080 },
      customTests: [
        async (page) => {
          // Test desktop-specific features
          const desktopNav = await page.$('.desktop-navigation');
          console.log('Desktop navigation present:', !!desktopNav);
        }
      ]
    },
    {
      name: 'Mobile Portrait',
      viewport: { width: 375, height: 667 },
      deviceScaleFactor: 2,
      isMobile: true,
      hasTouch: true,
      userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15',
      customTests: [
        async (page) => {
          // Test mobile hamburger menu
          await page.tap('.hamburger-menu');
          const mobileMenu = await page.waitForSelector('.mobile-menu', { visible: true });
          console.log('Mobile menu opens correctly:', !!mobileMenu);
        }
      ]
    }
  ];

  await tester.testResponsiveDesign('https://your-website.com', testCases);
  await tester.close();
}

runResponsiveTests();

Integration with Testing Frameworks

You can integrate responsive testing into popular testing frameworks like Jest:

const puppeteer = require('puppeteer');

describe('Responsive Web Design Tests', () => {
  let browser, page;

  beforeAll(async () => {
    browser = await puppeteer.launch({ headless: true });
    page = await browser.newPage();
  });

  afterAll(async () => {
    await browser.close();
  });

  test.each([
    [1920, 1080, 'Desktop'],
    [768, 1024, 'Tablet'],
    [375, 667, 'Mobile']
  ])('should display correctly on %s x %s (%s)', async (width, height, device) => {
    await page.setViewport({ width, height });
    await page.goto('https://your-website.com');

    // Test layout doesn't break
    const isLayoutBroken = await page.evaluate(() => {
      const elements = document.querySelectorAll('.container, .row, .col');
      return Array.from(elements).some(el => {
        const rect = el.getBoundingClientRect();
        return rect.width > window.innerWidth || rect.height <= 0;
      });
    });

    expect(isLayoutBroken).toBe(false);
  });
});

Best Practices for Screen Size Testing

  1. Test Critical Breakpoints: Focus on major CSS breakpoints (320px, 768px, 1024px, 1200px+)

  2. Include Real Device Dimensions: Test actual device resolutions, not just generic sizes

  3. Verify Interactive Elements: Ensure buttons, forms, and navigation work across all sizes

  4. Check Text Readability: Verify font sizes and line heights are appropriate

  5. Test Performance: Monitor load times and rendering performance on smaller screens

  6. Automate Regular Testing: Run responsive tests as part of your CI/CD pipeline

When implementing viewport testing with Headless Chromium, consider leveraging how to set viewport in Puppeteer for more detailed viewport configuration options. For comprehensive testing workflows, you might also want to explore how to handle timeouts in Puppeteer to ensure your tests remain stable across different network conditions.

Command Line Testing

For quick manual testing, you can use Chrome's command line flags:

# Test specific viewport size
google-chrome --headless --disable-gpu --window-size=375,667 --screenshot=mobile.png https://your-website.com

# Test multiple sizes with a script
for size in "1920,1080" "768,1024" "375,667"; do
  google-chrome --headless --disable-gpu --window-size=$size --screenshot=test_$size.png https://your-website.com
done

Testing with WebScraping.AI API

For developers who prefer API-based solutions, WebScraping.AI provides headless browser capabilities that can be configured for different screen sizes:

# Test mobile viewport
curl "https://api.webscraping.ai/html?url=https://your-website.com&device=mobile&js=true"

# Test desktop viewport  
curl "https://api.webscraping.ai/html?url=https://your-website.com&device=desktop&js=true"

The API handles the complexity of browser management while providing the flexibility to test different viewport configurations programmatically.

Headless Chromium provides a robust, automated solution for testing web applications across different screen sizes. By implementing comprehensive viewport testing, you can ensure your applications deliver consistent user experiences across all devices and screen resolutions, catching responsive design issues before they reach production.

Try WebScraping.AI for Your Web Scraping Needs

Looking for a powerful web scraping solution? WebScraping.AI provides an LLM-powered API that combines Chromium JavaScript rendering with rotating proxies for reliable data extraction.

Key Features:

  • AI-powered extraction: Ask questions about web pages or extract structured data fields
  • JavaScript rendering: Full Chromium browser support for dynamic content
  • Rotating proxies: Datacenter and residential proxies from multiple countries
  • Easy integration: Simple REST API with SDKs for Python, Ruby, PHP, and more
  • Reliable & scalable: Built for developers who need consistent results

Getting Started:

Get page content with AI analysis:

curl "https://api.webscraping.ai/ai/question?url=https://example.com&question=What is the main topic?&api_key=YOUR_API_KEY"

Extract structured data:

curl "https://api.webscraping.ai/ai/fields?url=https://example.com&fields[title]=Page title&fields[price]=Product price&api_key=YOUR_API_KEY"

Try in request builder

Related Questions

Get Started Now

WebScraping.AI provides rotating proxies, Chromium rendering and built-in HTML parser for web scraping
Icon