Zum Hauptinhalt springen
Echtzeit-Radar & Feeds
Alle RSS Feeds ➔
👥 Community & Social
•
IT Security NachrichtenWohin fließt das Geld? – So arbeiten die Cyber-Cops aus Hamm(01.10.2026 um 05:46 Uhr)
•
Malware / Trojaner / VirenIT Security News Hourly Summary 2026-10-01 08h : 8 posts(01.10.2026 um 08:00 Uhr)
•
IT Security NachrichtenProton Easy Switch migriert jetzt aus Microsoft 365(01.10.2026 um 08:03 Uhr)
•
IT Security NachrichtenEin Jahr nach Einführungspflicht: E-Patientenakten füllen sich(01.10.2026 um 08:08 Uhr)
•
IT Security NachrichtenInfineon nimmt neue Fabrik in Thailand in Betrieb(01.10.2026 um 08:17 Uhr)
•
IT Security NachrichtenWenn Maschinen zu Zielen werden(01.10.2026 um 08:00 Uhr)
•••••
IT Security NachrichtenWohin fließt das Geld? – So arbeiten die Cyber-Cops aus Hamm(01.10.2026 um 05:46 Uhr)
•
Malware / Trojaner / VirenIT Security News Hourly Summary 2026-10-01 08h : 8 posts(01.10.2026 um 08:00 Uhr)
•
IT Security NachrichtenProton Easy Switch migriert jetzt aus Microsoft 365(01.10.2026 um 08:03 Uhr)
•
IT Security NachrichtenEin Jahr nach Einführungspflicht: E-Patientenakten füllen sich(01.10.2026 um 08:08 Uhr)
•
IT Security NachrichtenInfineon nimmt neue Fabrik in Thailand in Betrieb(01.10.2026 um 08:17 Uhr)
•
IT Security NachrichtenWenn Maschinen zu Zielen werden(01.10.2026 um 08:00 Uhr)
••••
Intelligence View
⚡ tsecurity.de Intelligence

Building a Comprehensive E2E Test Suite with Playwright: Lessons from 100+ Test Cases

The Journey While developing the platform for LiveSpaces, I identified a critical gap in our delivery workflow. As the application grew, our reliance on manual…

Beitrag
0
Seite
0
↗ Quelle (dev.to)
Social ReaktionenReagiere als Erste:r — dein Feedback zählt!




The Journey



While developing the platform for LiveSpaces, I identified a critical gap in our delivery workflow. As the application grew, our reliance on manual verification and our existing CI/CD checks proved insufficient. We needed a way to streamline release cycles without sacrificing quality.



I took the initiative to build a comprehensive End-to-End (E2E) test suite using Playwright, but I underestimated the complexity. It wasn't just about clicking buttons; it was about handling authentication flows, managing device states, and dealing with third-party integrations securely and reliably.



From authentication flows to device management, every feature presented unique challenges. Here is a retrospective on the architecture I built to solve these bottlenecks, the decisions I made, and the lessons I learned.









Prerequisites



This post focuses on advanced patterns and architectural decisions rather than basic installation. I assume you are familiar with the basics of Playwright or similar E2E tools (Cypress, Selenium).



If you are brand new to Playwright, I highly recommend checking out the official documentation first.



Quick Context:

The examples below use:




  • Framework: Playwright (Node.js)

  • Language: JavaScript/TypeScript

  • Pattern: Page Object Model (POM)







What I Built





100+ Test Cases Across 6 Major Areas




  1. Email Signup Flow — The complete journey from registration to business setup.

  2. OAuth Integration — Signup and signin flows with comprehensive mocking.

  3. Device Pairing — Device pairing logic using 6-character OTP codes.

  4. Device Management — Renaming, deleting, and managing device settings.

  5. Account Type Selection — Complex modal handling for onboarding flows.

  6. Business Information Forms — Industry, demographics, and capacity selection logic.







Key Challenges & Solutions





Challenge 1: Email Verification in Tests



The Problem: I could not access real email inboxes (Gmail/Outlook) reliably in automated tests. Using real inboxes made the tests slow, flaky, and prone to security blocks.



The Solution: I implemented a smart mocking strategy.




  • Development: I mocked the email verification endpoint to return a fixed success response.

  • Production/Integration: I documented hooks for services like Mailosaur for when real delivery testing is strictly necessary.



Code Example:




// Mock the verification code endpoint to bypass email delivery
await page.route('**/api/verification/send', route => {
route.fulfill({
status: 200,
body: JSON.stringify({
success: true,
code: '123456' // Fixed mock code for testing
})
});
});









Challenge 2: OAuth Testing



The Problem: Automating third-party providers (Google, Facebook) is often a violation of their Terms of Service. Furthermore, dealing with 2FA, captchas, or external popups leads to incredibly flaky tests.



The Solution: Comprehensive OAuth flow mocking. Instead of visiting the provider, I intercepted the callback that the provider would have sent to our application.



Code Example:




// Mock the OAuth callback
await page.route('**/api/auth/oauth/callback**', route => {
const url = new URL(route.request().url());

// If the app is trying to exchange a token, give it a mock user
if (url.searchParams.has('access_token')) {
route.fulfill({
status: 200,
body: JSON.stringify({
jwt: 'mock_jwt_token',
user: { id: 123, email: '[email protected]' }
})
});
}
});









Challenge 3: Integration vs. Mocked Tests



The Problem: I needed fast feedback loops during development (mocks) but actual verification before deployment (integration). I didn't want to maintain two separate test suites.



The Solution: Dual-mode test files. I designed the tests to auto-detect the presence of an authentication token in the environment variables to decide whether to mock network requests or hit the real backend.



The Logic Flow:



Dual-mode flowchart



Code Example:




const AUTH_TOKEN = process.env.AUTH_TOKEN;
const USE_REAL_BACKEND = !!AUTH_TOKEN;

test.beforeEach(async ({ page }) => {
if (USE_REAL_BACKEND) {
console.log('Using REAL backend');
// No mocks; allow requests to pass through
} else {
console.log('Using MOCKED mode');
// Apply mocks defined in separate helper files
await applyNetworkMocks(page);
}
});









Challenge 4: Test Data Cleanup



The Problem: Integration tests running against a real backend leave behind "zombie" data (e.g., created devices), causing subsequent tests to fail due to duplicate name errors or database bloat.



The Solution:




  • Strict try/finally blocks for guaranteed cleanup within the test.

  • (Pro Tip: For larger suites, moving this logic into Playwright Fixtures is the preferred pattern).



Code Example:




test('should create and manage device', async ({ page, request }) => {
let testDeviceId = null;

try {
// 1. Create test device via API
const response = await request.post(`${API_URL}/devices`, {
headers: { Authorization: `Bearer ${AUTH_TOKEN}` },
data: { name: 'Test Device', code: 'TEST123' }
});
testDeviceId = (await response.json()).data.id;

// 2. Test the UI flow interacting with this device...
await page.reload();
await expect(page.getByText('Test Device')).toBeVisible();

} finally {
// 3. Always cleanup, even if the assertion above fails
if (testDeviceId) {
await request.delete(`${API_URL}/devices/${testDeviceId}`, {
headers: { Authorization: `Bearer ${AUTH_TOKEN}` }
});
}
}
});









Challenge 5: Async Component Loading



The Problem: Third-party components (maps, heavy charts) often load asynchronously. Tests were failing because they tried to interact with elements before they were truly interactive.



The Solution: Utilizing Playwright's Web-First Assertions. Unlike manual timeouts (waitForTimeout), these assertions automatically retry until the condition is met or the timeout is reached.



Code Example:




const mapInput = page.getByTestId('map-input');

// Bad Practice:
// await page.waitForTimeout(1000);

// Best Practice: Wait for state, not time
// This automatically waits for the element to be in the DOM, visible, AND enabled
await expect(mapInput).toBeEnabled({ timeout: 10000 });

// Now safe to interact
await mapInput.click();









Challenge 6: Portal-Based UI Components



The Problem: Modern UI libraries (like Radix UI or Headless UI) often render dropdowns and modals in "portals" at the bottom of the <body> tag, physically outside the component that triggered them.



The Solution:




  • I used specific ARIA roles.

  • I scoped locators correctly to escape the current container and search the document root.



Code Example:




// The dropdown trigger is in the main container
await page.getByRole('button', { name: 'Options' }).click();

// The menu itself is in a portal at the document root
// We wait for the menu specifically to be visible
const menu = page.getByRole('menu');
await expect(menu).toBeVisible();

// Click the item inside the menu
await menu.getByRole('menuitem', { name: 'Rename' }).click();












What Worked Really Well






1. Page Object Model (POM) Pattern



Centralizing selectors in reusable classes was the single highest-ROI decision I made. When the UI changed (and it did often), I updated one file instead of 20.



The Architecture:



Page Object Model architecture



Code Example:




// pages/AuthPage.js
export class AuthPage {
constructor(page) {
this.page = page;
this.emailInput = page.getByPlaceholder('[email protected]');
this.loginButton = page.getByRole('button', { name: 'Login' });
}

async login(email, password) {
await this.emailInput.fill(email);
await this.loginButton.click();
}
}









2. Comprehensive Documentation



Tests without documentation become legacy code the moment they are written. I created 15+ markdown files covering "How to run," "How to mock," and "How to debug." This dramatically reduced onboarding time for future developers and prevented "fear of the test suite."






3. Error Case Testing



Testing the "Happy Path" is easy. Testing the "Sad Path" is where value lies. I systematically mocked 404s, 500s, and network timeouts to ensure the UI handled errors gracefully (e.g., showing Toast notifications) rather than crashing.






4. Dual-Mode Testing



By using environment variables to switch between Mocks and Real Backends, a single test file serves as both a Unit-like Test (Mocked: fast, stable, deterministic) and an Integration Test (Real: slower, verifies backend contracts).









Key Learnings & Tips




  1. Mock External Services: Never test Google's login page. Mock the OIDC response. You are testing your app, not Google's uptime.

  2. Prioritize User-Visible Locators: Use getByRole and getByText over CSS classes (.btn-primary). This mimics how users find elements and makes tests resilient to styling refactors.

  3. Always Cleanup: Test data pollution causes flaky tests. If you create it, delete it (preferably in a finally block or a fixture).

  4. Wait Properly: Never use waitForTimeout(5000). If you find yourself doing this, you are likely missing a proper state assertion or an await expect(...).









The Numbers




  • Test Files: 10+

  • Test Cases: 100+

  • Lines of Code: ~5,000+

  • Coverage Areas: 6 major features

  • Confidence Level: High






Further Reading



If you want to dive deeper into the concepts mentioned here, I recommend these resources:








This post is based on real-world experience building a production E2E suite. All code examples are simplified for clarity.

2. Cyber Threat Intelligence & Forensik

CTI Threat Relationship Graph2 Knoten / 1 Relationen
CVE / Incident Software MITRE ATT&CK CWE Weakness IoC
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Building a Comprehensive E2E Test Suite with Playwright: Lessons from 100+ Test Cases

Thematisch verwandte Begriffe: Building, Comprehensive, Test, Suite · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

💬 Kommentare werden geladen…
Zum Aktualisieren ziehen
tsecurity.de Icon
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag