Guide

Screenshot Testing for E-commerce: Cart, Checkout, and Product Pages

Ensure your e-commerce site looks perfect across all customer touchpoints with automated visual testing.

Asad AliOctober 28, 20257 min read

E-commerce sites have unique visual testing challenges—product images, dynamic pricing, cart states, and checkout flows all need verification. This guide covers screenshot strategies for comprehensive e-commerce testing.

E-commerce Testing Challenges

Dynamic Content

  • Product prices change
  • Stock levels fluctuate
  • Personalized recommendations
  • Sale banners and promotions
  • Cart quantities and totals

Critical Pages

Page Testing Priority
Product detail page High
Category/listing page High
Cart Critical
Checkout Critical
Order confirmation Critical
Homepage Medium
Account pages Medium

Product Page Testing

Capture Product Galleries

async function testProductPage(productUrl) {
  // Main product image
  const mainImage = await captureScreenshot(productUrl, {
    viewport: { width: 1440, height: 900 },
    waitFor: '.product-image',
  });
  
  // Test image gallery navigation
  const galleryImages = await captureWithInteraction(productUrl, [
    { action: 'click', selector: '.gallery-thumbnail:nth-child(2)' },
    { action: 'wait', duration: 500 },
    { action: 'screenshot', name: 'gallery-image-2' },
    { action: 'click', selector: '.gallery-thumbnail:nth-child(3)' },
    { action: 'wait', duration: 500 },
    { action: 'screenshot', name: 'gallery-image-3' },
  ]);
  
  return { mainImage, galleryImages };
}

Test Product Variants

async function testProductVariants(productUrl, variants) {
  const screenshots = {};
  
  for (const variant of variants) {
    // Select variant (size, color, etc.)
    const screenshot = await captureWithInteraction(productUrl, [
      { action: 'click', selector: `[data-variant="${variant}"]` },
      { action: 'wait', duration: 300 },
      { action: 'screenshot' },
    ]);
    
    screenshots[variant] = screenshot;
  }
  
  return screenshots;
}

// Usage
const variants = ['red', 'blue', 'black'];
const variantScreenshots = await testProductVariants(
  'https://store.com/product/t-shirt',
  variants
);

Out of Stock States

const OUT_OF_STOCK_TEST_PRODUCTS = [
  'https://store.com/product/out-of-stock-item',
  'https://store.com/product/low-stock-item',
];

async function testStockStates() {
  const results = [];
  
  for (const url of OUT_OF_STOCK_TEST_PRODUCTS) {
    const screenshot = await captureScreenshot(url);
    
    // Verify out of stock styling is visible
    const hasOutOfStockLabel = await checkElement(url, '.out-of-stock-label');
    const buyButtonDisabled = await checkElement(url, 'button[disabled]');
    
    results.push({
      url,
      screenshot,
      hasLabel: hasOutOfStockLabel,
      buttonDisabled: buyButtonDisabled,
    });
  }
  
  return results;
}

Cart Testing

Empty Cart

async function testEmptyCart() {
  // Clear any existing cart
  await clearCart();
  
  const screenshot = await captureScreenshot('https://store.com/cart', {
    waitFor: '.empty-cart',
  });
  
  return screenshot;
}

Cart with Items

async function testCartWithItems() {
  // Add items to cart
  await addToCart([
    { product: 'SKU-001', quantity: 1 },
    { product: 'SKU-002', quantity: 2 },
  ]);
  
  const screenshot = await captureScreenshot('https://store.com/cart', {
    waitFor: '.cart-items',
  });
  
  // Verify quantities and totals are visible
  return screenshot;
}

Cart Updates

async function testCartQuantityChange() {
  await addToCart([{ product: 'SKU-001', quantity: 1 }]);
  
  const screenshots = await captureWithInteraction('/cart', [
    { action: 'screenshot', name: 'initial' },
    { action: 'click', selector: '.quantity-increase' },
    { action: 'wait', duration: 500 },
    { action: 'screenshot', name: 'after-increase' },
    { action: 'type', selector: '.quantity-input', text: '5' },
    { action: 'wait', duration: 500 },
    { action: 'screenshot', name: 'quantity-5' },
  ]);
  
  return screenshots;
}

Checkout Flow Testing

Multi-Step Checkout

async function testCheckoutFlow() {
  // Set up cart
  await addToCart([{ product: 'SKU-001', quantity: 1 }]);
  
  const checkoutScreenshots = [];
  
  // Step 1: Cart review
  checkoutScreenshots.push(await captureScreenshot('/checkout', {
    waitFor: '.checkout-step-1',
    name: 'step-1-cart',
  }));
  
  // Step 2: Shipping info
  await fillForm('.shipping-form', TEST_SHIPPING_INFO);
  checkoutScreenshots.push(await captureScreenshot('/checkout/shipping', {
    waitFor: '.checkout-step-2',
    name: 'step-2-shipping',
  }));
  
  // Step 3: Payment
  checkoutScreenshots.push(await captureScreenshot('/checkout/payment', {
    waitFor: '.checkout-step-3',
    name: 'step-3-payment',
  }));
  
  // Step 4: Review
  checkoutScreenshots.push(await captureScreenshot('/checkout/review', {
    waitFor: '.checkout-step-4',
    name: 'step-4-review',
  }));
  
  return checkoutScreenshots;
}

Guest vs Logged-In Checkout

async function testCheckoutStates() {
  const results = {};
  
  // Guest checkout
  await clearSession();
  results.guest = await captureScreenshot('/checkout', {
    waitFor: '.guest-checkout',
  });
  
  // Logged-in checkout
  await login(TEST_USER);
  results.loggedIn = await captureScreenshot('/checkout', {
    waitFor: '.user-checkout',
  });
  
  // Saved address loaded
  await selectSavedAddress();
  results.savedAddress = await captureScreenshot('/checkout', {
    waitFor: '.saved-address',
  });
  
  return results;
}

Error States

async function testCheckoutErrors() {
  const errorScreenshots = [];
  
  // Invalid card
  await fillPaymentForm({
    cardNumber: '4000000000000002', // Decline test card
  });
  await submitCheckout();
  errorScreenshots.push(await captureScreenshot('/checkout/payment', {
    waitFor: '.payment-error',
    name: 'payment-declined',
  }));
  
  // Expired card
  await fillPaymentForm({
    expiry: '01/20',
  });
  await submitCheckout();
  errorScreenshots.push(await captureScreenshot('/checkout/payment', {
    waitFor: '.expiry-error',
    name: 'card-expired',
  }));
  
  // Invalid shipping
  await fillShippingForm({
    zipCode: '00000',
  });
  errorScreenshots.push(await captureScreenshot('/checkout/shipping', {
    waitFor: '.shipping-error',
    name: 'invalid-shipping',
  }));
  
  return errorScreenshots;
}

Mobile Testing

Mobile Product Pages

const MOBILE_PRODUCTS = [
  '/product/featured-item',
  '/product/best-seller',
];

async function testMobileProductPages() {
  const results = [];
  
  for (const url of MOBILE_PRODUCTS) {
    results.push({
      url,
      portrait: await captureScreenshot(url, {
        viewport: { width: 375, height: 812 },
      }),
      landscape: await captureScreenshot(url, {
        viewport: { width: 812, height: 375 },
      }),
    });
  }
  
  return results;
}

Mobile Checkout

async function testMobileCheckout() {
  await addToCart([{ product: 'SKU-001', quantity: 1 }]);
  
  const screenshots = [];
  const steps = ['/cart', '/checkout', '/checkout/shipping', '/checkout/payment'];
  
  for (const step of steps) {
    screenshots.push(await captureScreenshot(step, {
      viewport: { width: 375, height: 812 },
      fullPage: true,
    }));
  }
  
  return screenshots;
}

Price Verification

Capture Price Display

async function testPriceDisplay(productUrl) {
  const screenshot = await captureScreenshot(productUrl, {
    waitFor: '.product-price',
  });
  
  // Also verify specific price elements
  const priceElement = await captureElement(productUrl, '.product-price');
  const salePrice = await captureElement(productUrl, '.sale-price');
  const regularPrice = await captureElement(productUrl, '.regular-price');
  
  return { full: screenshot, priceElement, salePrice, regularPrice };
}

Sale/Promotion States

async function testPromotionalPricing() {
  // Regular price
  const regular = await captureScreenshot('/product/item', {
    name: 'regular-price',
  });
  
  // With promo code
  await applyPromoCode('SAVE20');
  const withPromo = await captureScreenshot('/cart', {
    name: 'with-promo',
  });
  
  // Flash sale styling
  const flashSale = await captureScreenshot('/product/flash-sale-item', {
    name: 'flash-sale',
  });
  
  return { regular, withPromo, flashSale };
}

Category/Listing Pages

Grid vs List View

async function testListingViews(categoryUrl) {
  const screenshots = {};
  
  // Grid view
  screenshots.grid = await captureScreenshot(`${categoryUrl}?view=grid`, {
    fullPage: true,
  });
  
  // List view
  screenshots.list = await captureScreenshot(`${categoryUrl}?view=list`, {
    fullPage: true,
  });
  
  return screenshots;
}

Filters and Sorting

async function testFiltersAndSort(categoryUrl) {
  const screenshots = [];
  
  // Apply price filter
  screenshots.push(await captureScreenshot(`${categoryUrl}?price=0-50`, {
    name: 'price-filter',
  }));
  
  // Apply size filter
  screenshots.push(await captureScreenshot(`${categoryUrl}?size=large`, {
    name: 'size-filter',
  }));
  
  // Sort by price low to high
  screenshots.push(await captureScreenshot(`${categoryUrl}?sort=price-asc`, {
    name: 'sort-price-asc',
  }));
  
  return screenshots;
}

Test Organization

Page Object Pattern

class ProductPageTest {
  constructor(baseUrl) {
    this.baseUrl = baseUrl;
  }
  
  async captureMain() {
    return captureScreenshot(this.baseUrl);
  }
  
  async captureVariant(variant) {
    return captureWithInteraction(this.baseUrl, [
      { action: 'click', selector: `[data-variant="${variant}"]` },
      { action: 'screenshot' },
    ]);
  }
  
  async captureZoom() {
    return captureWithInteraction(this.baseUrl, [
      { action: 'hover', selector: '.product-image' },
      { action: 'screenshot' },
    ]);
  }
}

class CheckoutFlowTest {
  async runFullFlow() {
    const screenshots = {};
    screenshots.cart = await captureScreenshot('/cart');
    screenshots.checkout = await captureScreenshot('/checkout');
    // ... more steps
    return screenshots;
  }
}

Best Practices

1. Use Test Data

const TEST_PRODUCTS = {
  simple: '/product/basic-tshirt',
  variants: '/product/multicolor-jacket',
  outOfStock: '/product/sold-out-item',
  onSale: '/product/clearance-item',
};

2. Handle Dynamic Content

async function captureWithMasking(url) {
  const screenshot = await captureScreenshot(url, {
    maskElements: [
      '.recommended-products',
      '.recently-viewed',
      '.shipping-eta',
    ],
  });
  return screenshot;
}

3. Test Critical Paths Daily

// Priority testing schedule
const DAILY_TESTS = ['cart', 'checkout', 'product-detail'];
const WEEKLY_TESTS = ['category', 'search', 'account'];

Conclusion

E-commerce screenshot testing requires:

  1. Product pages - Variants, galleries, pricing
  2. Cart - Empty, populated, quantity changes
  3. Checkout - All steps, error states, guest/logged-in
  4. Mobile - Critical flow on mobile devices
  5. Dynamic content - Mask or exclude changing elements

Automated visual testing catches layout issues before customers encounter them, protecting conversion rates and brand perception.


Ready to test your e-commerce site?

Get your free API key → - 100 free screenshots to get started.

See also:

e-commerce
testing
visual testing
automation

About the Author

Asad Ali

Asad Ali

Full-Stack Developer and Founder of ZTabs with 8+ years of experience building scalable web applications and APIs. Specializes in performance optimization, SaaS development, and modern web technologies.

Credentials: Founder & CEO at ZTabs, Full-Stack Developer, Expert in Next.js, React, Node.js, and API optimization

Frequently Asked Questions

How do I capture the cart page without real inventory mutations?

Seed a dedicated test account with a fixed cart state via an API call before capture, or use a feature flag that pins the cart to a known fixture. Either pattern keeps visual tests deterministic across runs. Avoid capturing a real user's cart — timing and inventory changes produce false regressions.

Should I test checkout at multiple pricing/tax locales?

If you ship to multiple regions, yes. Geo-pin the capture via the user-agent locale or an explicit Accept-Language header, and verify each region's price display, currency symbol, and tax row. Missing a region in visual tests is how pricing bugs reach production in that region.

Dynamic promotional banners keep breaking my visual diffs. Fix?

Strip them via a CSS selector in the element-removal list, or capture against a staging URL where promos are disabled. Banners are designed to rotate — they will always trigger diffs on product pages. Isolate them from the capture so only real layout regressions fire the alert.

Ready to capture your first screenshot?

Get started with 100 free screenshots. No credit card required.

Related Articles