Eine Testsuite, alle Bildschirmgrößen
Ein E2E-Test, der nur die Standardauflösung prüft, übersieht die meisten Layout-Fehler, die echte Nutzer sehen. Dieser Artikel zeigt, wie ihr Cypress und Playwright so konfiguriert, dass dieselbe Testsuite automatisch über mobile, Tablet- und Desktop-Breakpoints läuft, Navigation, Überlappungen und Textabschneidungen zuverlässig erkennt und Screenshot-Baselines je Bildschirmgröße pflegt, ohne Tests zu duplizieren.
Inhaltsverzeichnis
- 1. Warum responsive Tests eigene Breakpoints brauchen
- 2. Breakpoints definieren: die Tailwind-Skala als Testgrundlage
- 3. Cypress: Viewport-Commands und parametrisierte Tests
- 4. Playwright: Projects und Device-Presets für Breakpoints
- 5. Navigation testen: Hamburger-Menü vs. Desktop-Nav
- 6. Layout-Bugs erkennen: Overflow, Überlappung, Text-Truncation
- 7. Viewport-spezifische Screenshot-Baselines für visuelle Regression
- 8. CI-Matrix: Breakpoints parallel in der Pipeline testen
- 9. Responsive-Test-Patterns im direkten Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum responsive Tests eigene Breakpoints brauchen
Die meisten E2E-Suiten laufen mit einem einzigen, festen Viewport, oft 1280x720 oder die Standardgröße des Test-Runners. Das reicht aus, um zu prüfen, ob eine Funktion grundsätzlich funktioniert, sagt aber nichts darüber aus, ob dieselbe Seite auf einem Smartphone nutzbar ist. Gerade in E-Commerce-Shops kommt ein Großteil des Traffics über mobile Geräte, und genau dort brechen Layouts am häufigsten: Buttons wandern unter den Fold, Formulare werden unbedienbar, oder das Hauptmenü verschwindet komplett, weil der zuständige Alpine.js-Toggle nie unter realistischen Bedingungen getestet wurde.
Responsive-Testing bedeutet, dieselbe Testsuite gezielt an mehreren Bildschirmgrößen auszuführen, statt Breakpoints nur manuell im Browser durchzuklicken. Der entscheidende Hebel dabei ist, die Testgrößen nicht willkürlich zu wählen, sondern an den Breakpoints auszurichten, die auch im CSS verwendet werden, also an Tailwinds sm/md/lg/xl-Skala. So decken Tests exakt die Übergänge ab, an denen sich das Layout tatsächlich verändert, statt zufällige Zwischengrößen zu prüfen, an denen ohnehin nichts umbricht.
2. Breakpoints definieren: die Tailwind-Skala als Testgrundlage
Tailwind CSS definiert Breakpoints als Mindestbreiten: sm ab 640px, md ab 768px, lg ab 1024px, xl ab 1280px und 2xl ab 1536px. Diese Werte sind im Hyvä-Theme über Tailwind v4 als CSS-First-Konfiguration im @theme-Block hinterlegt. Für Tests ergibt es wenig Sinn, eigene Pixelwerte zu erfinden. Stattdessen sollte ein Breakpoint knapp unterhalb und knapp oberhalb jeder Grenze getestet werden, zum Beispiel 375px für mobile Geräte unterhalb von sm, 768px exakt am md-Übergang und 1280px für Desktop ab xl.
Die zentrale Regel gegen Duplikation: Breakpoints werden genau einmal definiert, in einer gemeinsamen Konfigurationsdatei, und von dort sowohl in Cypress- als auch in Playwright-Tests importiert. Ändert sich ein Breakpoint im Tailwind-Theme, muss nur eine Stelle im Testcode angepasst werden. Ohne diese zentrale Quelle driften Testviewports und tatsächliche CSS-Breakpoints über Zeit auseinander, und Tests prüfen am Ende Größen, die im Produktivcode gar keine Rolle mehr spielen.
/* Tailwind v4 CSS-first breakpoint tokens - the single source of truth for the theme */
@theme {
--breakpoint-sm: 640px;
--breakpoint-md: 768px;
--breakpoint-lg: 1024px;
--breakpoint-xl: 1280px;
--breakpoint-2xl: 1536px;
}
/* Test viewports mirror these tokens, offset just below/above each boundary */
3. Cypress: Viewport-Commands und parametrisierte Tests
cy.viewport(width, height) setzt die Fenstergröße für den aktuellen Test und akzeptiert wahlweise Pixelwerte oder vordefinierte Presets wie iphone-x oder ipad-2. Der Befehl allein reicht aber nicht aus, um Duplikation zu vermeiden. Das saubere Pattern ist, eine Liste von Breakpoints zu definieren und mit Object.entries().forEach() pro Breakpoint einen eigenen describe-Block zu erzeugen. Cypress selbst bietet kein natives describe.each, aber die Schleife über ein Array liefert exakt dasselbe Ergebnis: derselbe Testcode läuft unverändert für jede Bildschirmgröße, nur der Viewport und die erwartete Sichtbarkeit ändern sich.
Wichtig ist, den Viewport in einem beforeEach zu setzen und nicht erst nach dem cy.visit(), weil einige Layout-Berechnungen und Media-Query-Listener schon beim initialen Rendern greifen. Assertions innerhalb des parametrisierten Tests sollten breakpointabhängige Bedingungen explizit abfragen, etwa ob die Fensterbreite unter dem md-Wert liegt, statt für jede Größe einen separaten, fast identischen Testfall von Hand zu schreiben.
// cypress/support/breakpoints.js - single source of truth, mirrors the Tailwind scale
export const BREAKPOINTS = {
mobile: { width: 375, height: 667 }, // below sm
tablet: { width: 768, height: 1024 }, // md
desktop: { width: 1280, height: 800 }, // xl
};
// cypress/e2e/responsive/nav.cy.js
import { BREAKPOINTS } from '../../support/breakpoints';
Object.entries(BREAKPOINTS).forEach(([name, size]) => {
describe(`Navigation at ${name} (${size.width}x${size.height})`, () => {
beforeEach(() => {
cy.viewport(size.width, size.height);
cy.visit('/');
});
it('shows the correct nav pattern for this breakpoint', () => {
if (size.width < 768) {
cy.get('[data-testid="nav-hamburger"]').should('be.visible');
cy.get('[data-testid="nav-desktop-links"]').should('not.be.visible');
} else {
cy.get('[data-testid="nav-hamburger"]').should('not.be.visible');
cy.get('[data-testid="nav-desktop-links"]').should('be.visible');
}
});
});
});
4. Playwright: Projects und Device-Presets für Breakpoints
Playwright löst dasselbe Problem auf Konfigurationsebene statt im Testcode. In playwright.config.js definiert das projects-Array beliebig viele Ausführungskontexte, jeder mit eigenem Viewport, eigenem Browser oder sogar eigenem device-Preset aus dem devices-Modul. Derselbe Satz an Spec-Dateien wird dann automatisch gegen jedes Projekt ausgeführt, ganz ohne forEach-Schleifen im Test selbst. Das ist der große strukturelle Unterschied zu Cypress: Die Parametrisierung passiert im Runner, nicht im Testcode.
Für Breakpoint-Tests kombiniert man am besten eigene Viewport-Werte mit den devices-Presets, die zusätzlich realistische User-Agent-Strings, Touch-Unterstützung und Pixel-Ratio mitbringen. Ein Projekt für mobile-375 mit iPhone 13-Preset testet damit nicht nur die Breite, sondern auch Touch-Events und Viewport-Meta-Verhalten, die auf echten Geräten zu abweichendem Rendering führen können. Über npx playwright test --project=mobile-375 lässt sich gezielt ein einzelner Breakpoint isoliert ausführen, was besonders beim lokalen Debuggen einzelner Layout-Bugs Zeit spart.
// playwright.config.js - one project per breakpoint, same spec files reused everywhere
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests/responsive',
projects: [
{
name: 'mobile-375',
use: { ...devices['iPhone 13'], viewport: { width: 375, height: 667 } },
},
{
name: 'tablet-768',
use: { viewport: { width: 768, height: 1024 } },
},
{
name: 'desktop-1280',
use: { viewport: { width: 1280, height: 800 } },
},
{
name: 'desktop-1536',
use: { viewport: { width: 1536, height: 900 } },
},
],
});
// tests/responsive/nav.spec.js - runs unchanged against every project defined above
import { test, expect } from '@playwright/test';
test('nav pattern matches the active viewport', async ({ page, viewport }) => {
await page.goto('/');
const isMobile = (viewport?.width ?? 0) < 768;
const hamburger = page.getByTestId('nav-hamburger');
await expect(hamburger).toBeVisible({ visible: isMobile });
});
5. Navigation testen: Hamburger-Menü vs. Desktop-Nav
Die Navigation ist das Element, das sich zwischen Breakpoints am stärksten strukturell verändert, nicht nur optisch. Unterhalb von md zeigt Hyvä typischerweise ein Hamburger-Icon, das per Alpine.js x-show ein Off-Canvas-Menü einblendet, während oberhalb von md die vollständige Desktop-Nav-Leiste direkt sichtbar ist. Ein Test, der nur die Desktop-Selektoren prüft, übersieht komplett, ob das mobile Menü überhaupt funktioniert, und ein Test, der nur auf mobile Größen läuft, übersieht Regressionen in der Desktop-Nav. Beide Pfade brauchen eigene, bedingte Assertions.
Über die reine Sichtbarkeit hinaus lohnt sich die Prüfung von aria-expanded am Hamburger-Button vor und nach dem Klick, da dieses Attribut sowohl für Screenreader als auch häufig für CSS-Transitions als Zustandsquelle dient. Bei Off-Canvas-Menüs ist zusätzlich Focus-Trapping relevant: Nach dem Öffnen sollte der Fokus in das Menü wandern, und Tab-Navigation sollte es nicht verlassen können, solange es geöffnet ist. Diese Verhaltensunterschiede zwischen den Breakpoints lassen sich in Playwright sauber über die viewport-Fixture abfragen und in Cypress über die im vorherigen Abschnitt gezeigte Breakpoint-Schleife.
6. Layout-Bugs erkennen: Overflow, Überlappung, Text-Truncation
Drei Fehlerklassen tauchen an spezifischen Breakpoints immer wieder auf. Horizontaler Overflow entsteht, wenn ein Element breiter ist als sein Container, etwa eine nicht umbrechende Tabelle oder ein zu breites Bild ohne max-width: 100%. Er lässt sich zuverlässig automatisiert erkennen, indem man document.documentElement.scrollWidth gegen window.innerWidth vergleicht. Ist der Scroll-Wert größer, existiert an dieser Breite ungewollter horizontaler Scroll, unabhängig davon, welches Element ihn verursacht.
Überlappende Elemente entstehen häufig bei absolut positionierten Badges, Sticky-Headern oder Dropdown-Menüs, die bei bestimmten Breiten nicht mehr genug Platz haben. Über getBoundingClientRect() lässt sich prüfen, ob sich die Rechtecke zweier Elemente überschneiden, die sich laut Design niemals überlappen dürfen. Text-Truncation-Bugs zeigen sich, wenn ein mit text-overflow: ellipsis gekürzter Text bei kleineren Breakpoints so stark abgeschnitten wird, dass er unverständlich wird. Ein einfacher, zuverlässiger Check vergleicht element.scrollWidth mit element.clientWidth und schlägt fehl, sobald der gekürzte Anteil einen definierten Schwellenwert übersteigt, statt nur zu prüfen, dass überhaupt Text vorhanden ist.
7. Viewport-spezifische Screenshot-Baselines für visuelle Regression
Funktionale Assertions wie Sichtbarkeit und Attribute erkennen strukturelle Fehler, aber keine rein optischen Regressionen wie verschobene Abstände oder falsche Farben. Dafür braucht es visuelle Regressionstests mit Screenshot-Vergleich, und die Baseline-Bilder müssen zwingend pro Breakpoint getrennt vorliegen. Ein einziges Baseline-Set ohne Breakpoint-Bezug führt zu ständigen False Positives, weil sich ein und dieselbe Komponente bei 375px anders anordnet als bei 1280px, obwohl beide Zustände korrekt sind.
Sowohl Playwright (toHaveScreenshot()) als auch Cypress mit einem Plugin wie cypress-image-diff hängen den Projekt- beziehungsweise Browsernamen automatisch an den Dateinamen der Baseline an, sodass nav.spec.js-mobile-375-chromium.png und nav.spec.js-desktop-1280-chromium.png unabhängig voneinander gepflegt werden. Wichtig für stabile Baselines: CSS-Animationen vor dem Screenshot deaktivieren, dynamische Inhalte wie Datum oder Warenkorb-Zähler maskieren, und Baselines nur bewusst und einzeln aktualisieren, statt pauschal die gesamte Bildersammlung neu zu erzeugen.
# Update visual regression baselines only for the breakpoints that actually changed
npx playwright test --project=mobile-375 --update-snapshots
npx playwright test --project=tablet-768 --update-snapshots
# Run the full breakpoint matrix and diff against the existing baselines
npx playwright test tests/responsive/visual.spec.js
# Cypress equivalent: cypress-image-diff writes one baseline set per viewport name
npx cypress run --spec "cypress/e2e/responsive/visual.cy.js" --env updateSnapshots=true
8. CI-Matrix: Breakpoints parallel in der Pipeline testen
Werden alle Breakpoints sequentiell in einem einzigen CI-Job ausgeführt, addiert sich die Laufzeit jedes Breakpoints zur Gesamtlaufzeit der Pipeline, und ein einzelner instabiler Test blockiert alle nachfolgenden Breakpoint-Läufe. Die robustere Lösung ist eine Matrix-Strategie: Jeder Breakpoint läuft als eigener, paralleler Job, mit dem Playwright-Projektnamen oder dem Cypress-Grep-Tag als Matrix-Variable. Schlägt ein Breakpoint fehl, laufen die übrigen trotzdem vollständig durch, und die Fehlermeldung ist sofort einem konkreten Breakpoint zugeordnet.
fail-fast: false ist dabei entscheidend, weil CI-Runner standardmäßig die gesamte Matrix abbrechen, sobald ein einzelner Job fehlschlägt. Für Responsive-Tests ist das kontraproduktiv, denn ein gescheiterter Mobile-Test sagt nichts über den Desktop-Zustand aus. Der Artefakt-Upload für fehlgeschlagene Screenshots pro Matrix-Eintrag beschleunigt zusätzlich die Fehlersuche, weil Reviewer den visuellen Diff direkt im CI-Interface öffnen können, ohne den Test lokal reproduzieren zu müssen.
# .github/workflows/responsive-tests.yml
name: Responsive E2E Tests
on: [pull_request]
jobs:
e2e:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
breakpoint: [mobile-375, tablet-768, desktop-1280, desktop-1536]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npx playwright install --with-deps chromium
- name: Run breakpoint project
run: npx playwright test --project=${{ matrix.breakpoint }}
- uses: actions/upload-artifact@v4
if: failure()
with:
name: playwright-report-${{ matrix.breakpoint }}
path: playwright-report/
9. Responsive-Test-Patterns im direkten Vergleich
Viele Teams testen responsive Layouts zunächst ad hoc, mit hartcodierten Pixelwerten in einzelnen Testdateien und ohne klare Struktur für Baselines oder CI-Parallelisierung. Die folgende Übersicht zeigt, welche Patterns tatsächlich robust sind und welche bei wachsender Testsuite schnell zu Wartungsproblemen führen.
| Testszenario | Unsicher / Fehleranfällig | Empfohlenes Pattern | Vorteil |
|---|---|---|---|
| Viewport-Definition | cy.viewport(390, 844) pro Testdatei |
Breakpoints aus zentraler Fixture importieren | Keine Drift zwischen CSS und Tests |
| Testabdeckung | Ein Test für alle Bildschirmgrößen | Parametrisiert je Breakpoint (forEach / Projects) | Deckt reale Layout-Bugs ab |
| Navigation-Assertions | Nur Desktop-Selektoren geprüft | Bedingte Assertions für Hamburger- und Desktop-Nav | Erkennt kaputte mobile Menüs |
| Screenshot-Baselines | Ein Baseline-Set ohne Breakpoint-Bezug | Baseline-Dateiname inkl. Breakpoint-Suffix | Vermeidet False Positives |
| CI-Ausführung | Alle Breakpoints sequentiell in einem Job | Matrix-Strategie mit einem Job pro Breakpoint | Schnellere Pipeline, isolierte Fehler |
| Geräteemulation | Nur die Viewport-Größe simuliert | devices-Presets mit echten UA-Strings und Touch |
Realistischeres Interaktionsverhalten |
Der gemeinsame Nenner aller empfohlenen Patterns ist Zentralisierung: eine Breakpoint-Quelle, eine parametrisierte Testlogik, eine klare Namenskonvention für Baselines. Wer diese drei Punkte konsequent umsetzt, kann die Testsuite um weitere Breakpoints erweitern, ohne bestehenden Testcode anzufassen, sondern lediglich die zentrale Konfiguration.
Mironsoft
E2E-Testautomatisierung, visuelle Regressionstests und CI-Pipelines für Magento- und Hyvä-Shops
Responsive-Tests, die alle Breakpoints wirklich abdecken?
Wir bauen parametrisierte Cypress- und Playwright-Suiten für euren Shop auf, richten Screenshot-Baselines je Breakpoint ein und integrieren die komplette Matrix in eure CI-Pipeline, damit Layout-Bugs auffallen, bevor Kunden sie sehen.
Test-Setup
Breakpoint-Fixtures, parametrisierte Specs und Playwright-Projects einrichten
Visuelle Regression
Screenshot-Baselines je Breakpoint, Masking dynamischer Inhalte
CI-Integration
Matrix-Strategie, parallele Jobs und Artefakt-Reports für jeden Breakpoint
10. Zusammenfassung
Responsive-Testing über Breakpoints hinweg löst ein einfaches, aber folgenreiches Problem: Ein Test bei einer einzigen Bildschirmgröße sagt nichts über die tatsächliche Nutzererfahrung auf den restlichen Geräteklassen aus. Breakpoints, die an Tailwinds sm/md/lg/xl-Skala ausgerichtet sind, aus einer zentralen Fixture importiert und sowohl in Cypress-Schleifen als auch in Playwright-Projects wiederverwendet werden, verhindern Duplikation und Drift zwischen CSS und Testcode. Navigation, horizontaler Overflow, überlappende Elemente und Text-Truncation lassen sich mit gezielten, breakpointabhängigen Assertions zuverlässig automatisiert erkennen.
Screenshot-Baselines sind nur dann verlässlich, wenn sie strikt pro Breakpoint getrennt geführt werden, und eine CI-Matrix mit fail-fast: false stellt sicher, dass ein gescheiterter Mobile-Test nicht die Auswertung der Desktop-Ergebnisse blockiert. Wer diese Bausteine einmal sauber aufsetzt, kann die Testsuite um neue Breakpoints erweitern, indem lediglich die zentrale Konfiguration angepasst wird, ganz ohne bestehenden Testcode zu duplizieren.
Responsive-Testing über Breakpoints hinweg - Das Wichtigste auf einen Blick
Zentrale Breakpoints
Eine Fixture-Datei, ausgerichtet an Tailwinds sm/md/lg/xl-Skala, importiert in Cypress und Playwright.
Parametrisierte Tests
forEach über Breakpoints in Cypress, projects-Array in Playwright, keine Testduplikation.
Layout-Bugs erkennen
Overflow via scrollWidth, Überlappung via getBoundingClientRect(), Truncation via Breitenvergleich.
CI-Matrix
Ein paralleler Job je Breakpoint, fail-fast: false, Artefakt-Upload für fehlgeschlagene Screenshots.