惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

D
DataBreaches.Net
N
Netflix TechBlog - Medium
P
Proofpoint News Feed
D
Docker
J
Java Code Geeks
L
LangChain Blog
Microsoft Security Blog
Microsoft Security Blog
The GitHub Blog
The GitHub Blog
I
InfoQ
Stack Overflow Blog
Stack Overflow Blog
云风的 BLOG
云风的 BLOG
Engineering at Meta
Engineering at Meta
MongoDB | Blog
MongoDB | Blog
月光博客
月光博客
T
Tailwind CSS Blog
M
MIT News - Artificial intelligence
Blog — PlanetScale
Blog — PlanetScale
Google DeepMind News
Google DeepMind News
腾讯CDC
罗磊的独立博客
U
Unit 42
爱范儿
爱范儿
Vercel News
Vercel News
MyScale Blog
MyScale Blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Stability & Maintainability at Scale (Playwright + TypeSc...
kadir · 2026-06-09 · via DEV Community

As a suite grows, two things decide whether it stays an asset or becomes a liability:
is it stable (does it fail only for real reasons?) and is it maintainable
(can you add the next flow without copy-paste?). This chapter is about the habits
that keep both true — demonstrated by adding comment and settings flows.

Code for this chapter is tagged ch-23 in the repo:
https://github.com/aktibaba/playwright-qa-course — see src/utils/unique.ts,
src/pages/SettingsPage.ts, and the new comment-ui / settings-ui specs.

Centralize the tricky bits

The flaky slug bug a few chapters back came from generating "unique" data that
wasn't unique across parallel workers. The lesson isn't "be careful" — it's put the
hard thing in one place so nobody gets it wrong again
:

// src/utils/unique.ts
let counter = 0;
export function uniqueId(prefix = "id"): string {
  counter += 1;
  return `${prefix}-${Date.now()}-${counter}-${Math.floor(Math.random() * 1e9)}`;
}

Now the article factory and the user factory both call uniqueId() — one proven
recipe, zero chances to reintroduce the collision. That's maintainability: the
correct way is the only way.

Wait for the right signal, not a guess

The settings screen loads the current user asynchronously, then fills the form.
Editing a field before that load lands would submit empty values over the real ones.
The stable fix is never a waitForTimeout — it's waiting for the actual readiness
signal:

// src/pages/SettingsPage.ts
async goto(): Promise<void> {
  await this.page.goto("/#/settings");
  await expect(this.updateButton).toBeVisible();
  await expect(this.username).not.toHaveValue(""); // the form has loaded
}

Encapsulating that wait in the Page Object means every settings test inherits the
stability for free — the test just calls goto().

New flows, same machinery

Adding comments and settings didn't require new infrastructure — they reuse the
fixtures and Page Objects we already have. A comment test reads as behavior:

test.use({ storageState: ".auth/playwright.json" });

test("post a comment and see it appear", async ({ makeArticle, articlePage }) => {
  const article = await makeArticle();           // seed via API
  await articlePage.goto(article.slug);
  const body = `Nice article ${Date.now()}`;
  await articlePage.postComment(body);           // act in UI
  await expect(articlePage.comment(body)).toBeVisible(); // verify in UI
});

The settings test goes further on isolation: it registers a fresh user through
the API and logs in as them, so changing a profile never contends with other tests
on the shared seed user. New surface, but the same registerUser, loginPage, and
settingsPage building blocks. That's what "scales" means here — the marginal cost
of the next flow is small.

…and another real bug

Writing the settings flow, the UI test failed — and so did a direct API check. The
SUT's update endpoint 500'd on every profile update:

// the original, buggy condition
if (password !== undefined || password !== "") {   // always true!
  loggedUser.password = await bcryptHash(password); // bcryptHash(undefined) -> 500
}

a !== x || a !== y is always true, so every update tried to hash an absent
password ("data and salt arguments required") — and on a real save would have
clobbered the user's password. One character — ||&& — fixed it. The suite
didn't just verify the settings screen; it proved the whole feature was broken.

Next up

Chapter 24 — Framework maturation & docs: we tidy the project, document how to
run and extend it, and round out coverage so a newcomer can be productive in
minutes. Tag: ch-24.

Following along? Star the repo
and tell me the one helper that removed the most flakiness from your suite.