-
Section 1: Getting Started
-
Section 2: Core Syntax and Types
-
Section 3: Strings and Numbers in Depth
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Objects and Arrays
-
Section 7: Maps, Sets, and Symbols
-
Section 8: Asynchronous JavaScript
-
Section 9: Object-Oriented and Prototypes
-
Section 10: The DOM
-
Section 11: Browser APIs
-
Section 12: Modern JavaScript (ES2015-ES2025)
-
Section 13: Functional Programming Patterns
-
Section 14: Error Handling and Debugging
-
Section 15: Testing
-
Section 16: Accessibility for JavaScript Developers
-
Section 17: Internationalization and Localization
-
Section 18: Performance
-
Section 19: Node.js Fundamentals
-
Section 20: Regular Expressions
-
Section 21: Design Patterns in JavaScript
-
Section 22: Security Basics
-
Section 23: Data Structures and Algorithms in JavaScript
-
Section 24: Practical Projects
-
Section 25: More Advanced Async Patterns
-
Section 26: More Object and Class Practice
-
Section 27: Working with Dates and Internationalization
-
Section 28: Web Components
-
Section 29: More DOM and Browser Practice
-
Section 30: Build Tooling for Vanilla JavaScript
-
Section 31: More Practice Projects
-
Section 32: Interview and Algorithm Practice
-
Section 33: Error Objects (MDN Reference)
-
Section 34: TypedArrays and Binary Data
-
Section 35: Reflection and Metaprogramming (MDN Reference)
-
Section 36: More Global Functions (MDN Reference)
127: End-to-End Testing with Playwright or Cypress
Should I go with Playwright or Cypress?
This is the debate of the year in the JS community. Honestly? Both are fantastic, but they approach the problem differently. Cypress runs directly inside the browser, which makes the developer experience incredible—you can see exactly what's happening in real-time. However, it has some limitations with things like multiple tabs or iframes because it's bound by the browser's security sandbox.
Playwright, on the other hand, uses the CDP (Chrome DevTools Protocol) to control the browser from the outside. I've found it's generally faster, handles multi-tab workflows much better, and has a more robust "auto-wait" system. If you're starting a project today, I usually lean toward Playwright. It feels more like how a real user interacts with a machine, and the ability to run tests across Chromium, Firefox, and WebKit simultaneously is a huge win.
What does a real E2E test actually look like in practice?
Stop thinking about testing individual functions; E2E is about testing the user's journey. Let's say you're building an e-commerce site. You don't care if the `addToCart` function works in isolation; you care that a user can find a product, click a button, and see that item in their cart. Here is how I'd write that using Playwright:
import { test, expect } from '@playwright/test';
test('user can add a limited edition sneaker to the cart', async ({ page }) => {
// Navigate to the product page
await page.goto('https://myshop.com/products/air-max-90');
// Select a size and add to cart
await page.selectOption('select#size', '10');
await page.click('button#add-to-cart');
// Go to the cart page
await page.click('a#cart-icon');
// Verify the item is actually there
const cartItem = page.locator('.cart-item-title');
await expect(cartItem).toContainText('Air Max 90');
// Ensure the price is correct
const totalPrice = page.locator('.total-price');
await expect(totalPrice).toHaveText('$120.00');
});
How do I stop my tests from failing randomly (flakiness)?
Flakiness is the absolute worst part of E2E testing. You'll run a test ten times, and it fails on the eleventh for no apparent reason. This usually happens because the test is faster than your app. You're trying to click a button that hasn't finished rendering yet.
My biggest piece of advice: never use hard-coded sleeps (like await page.waitForTimeout(3000)). That's a lazy habit that slows down your CI pipeline and still doesn't guarantee the element is ready. Instead, rely on "web-first assertions." In the example above, await expect(cartItem).toContainText(...) doesn't just check the text once; it polls the page for a few seconds until the condition is met. If your app is slow, Playwright waits. If it's fast, the test moves on immediately. It's much more resilient.
Can I skip the login screen for every single test?
If you have 50 tests and each one starts by typing a username and password into a login form, your test suite is going to be painfully slow. It's also a waste of resources.
The professional way to handle this is through "Storage State." You essentially perform the login once in a global setup phase, save the cookies and local storage to a JSON file, and then tell your tests to load that state before they start. You're effectively "injecting" the session into the browser.
// playwright.config.ts
export default defineConfig({
use: {
// Tell all tests to use this saved authentication state
storageState: 'auth.json',
},
});
I usually set up a separate "auth" script that runs before the main test suite. It logs in, saves auth.json, and then the rest of your tests start already logged in. It saves minutes of execution time per run.
📋 Practical Task
Exercise: Automating the Guest Checkout Flow for the Shoe Store
Your task is to write a Playwright test script that validates the "Guest Checkout" experience. You should avoid using a logged-in state for this specific test, as you are simulating a first-time visitor.
Requirements:
- Navigate to the
/products/runner-propage. - Select the "Blue" color option and "Size 11".
- Click "Add to Cart" and navigate to the checkout page.
- Fill out the guest checkout form with a dummy email (
tester@example.com) and a shipping address. - Click "Place Order".
- Assert that the final page contains a "Thank you for your order!" confirmation message.
Constraint: Do not use waitForTimeout(). Use Playwright's built-in assertions or locators to ensure the test is stable regardless of network latency.
There are no comments for now.